User Authentication from Scratch in Elixir and Phoenix
In my previous post about Phoenix, we built a very simple blog app. We only added the ability to post content, but there’s no user authentication system. In this post, we will add user authentication to the app.
NOTE: I’m using Elixir 1.2.4 with Phoenix 1.1.4 for this example.
Add a user model
The first thing we will do is add a User model. Our users will sign up with an email and a password. We will not be storing the password directly as plaintext, but in an hashed format. Let’s generate the model:
(Note: If you’re facing problems with the command, you might want to take a look at this issue to solve this. The API to define unique contraint is not available in Phoenix < v1.0.4.)
This adds a User model
and a migration
to create a
in the database.
Run the migration
using the command:
Now that we have added the User model,
let’s move on to creating a registration page.
Let’s add the registration routes to
Add this line after
resources "/posts" line
in the file:
This adds a
POST /registrations path
to handle the form submission.
Add a link to the register page
in the navigation section of
Next we add a
When we try to load the
Phoenix shows us an error page
complaining about a missing
Let’s add an empty view module.
Now, we are ready to add the HTML view to
Now if we go to the path
we will find a signup form.
If we submit the form,
we will get this error:
Let’s add the
to handle this request.
The function would look like this:
Here we’re capturing
the email and password into
and creating a
User changeset using it.
We will need to create
to save the user to database.
We’ll come back to that later.
First, let’s take care
of adding validations
Validating and persisting
We need to make some changes
before we can continue.
The first thing to do
is to add a virtual password attribute
This allows us to create a changeset
with the password attribute,
but it will not be persisted.
The schema block should now look like this:
Next we’ll change
to make password mandatory
instead of crypted_password.
We also need to add some validations
This function makes email and password mandatory,
validates uniqueness of email,
checks if email contains an
(this is a very crude format validation,
but should suffice for now),
and ensures password is longer than 5 characters.
If we submit the form now, we should see the same template re-rendered with the corresponding errors.
Now let’s return to the case where we have a valid changeset and need to persist the user to the database.
Here, we’re calling
which is in a module we haven’t written yet.
It hashes the password,
User record to database,
and returns the persisted object.
Then it redirects the user
to the home page
with the success flash message.
Now let’s implement the
This will contain a
that takes in a user changeset and a repo,
hash the password using Bcrypt
and then save the changeset to the database.
We haven’t implemented
the function to hash the password.
For this, we will use the
which provides functions
to hash passwords using bcrypt.
Add comeonin to
mix deps.get to install the new dependency.
Once this has been installed,
we can implement the
hashed_password/2 function like this:
If you submit valid email and password, you will now be redirected to the home page and shown the message, “your account has been created”.
Adding a login page
Now that we have added
the ability to create an account,
let’s add the login/logout feature.
The first thing is to add the routes.
Add these routes immediately after the registration page routes in
Next, let’s add the SessionController and create the login page.
Create a new template
and add this:
If you open the page
it would complain that
Blog.SessionView module is missing.
Let’s add an empty module
as we did with
This fixes the problem, and you can now see the login page.
Submitting the login form
Now we need to add a function
to handle submission of this form.
This is what the
create function looks like:
This function matches the session params
and passes them to
This function checks
if there is a user in the repo
with the matching password,
and returns a tuple
:ok and the user
if the email and password are correct.
Otherwise it returns the atom
We haven’t added the
Blog.Session module yet.
Let’s go ahead and write the code.
We can use
to check if the password matches the user’s password.
If the password is wrong,
or if a user does not exist with that email,
we return the atom
If authentication succeeds,
we return a tuple
:ok and the user.
Adding some helper functions for the view
You will notice that even when a user is logged in, they can see the register link in the navbar, and access the page. We need to hide this, and show the logout button when a user is logged in.
Let’s add a couple of functions
and make them available in the view.
The first function is
that returns the currently logged in user.
It uses the
:current_user session variable
we set during login.
We can now use this
to add another helper function,
Now we can make these available in the views
by adding this line at the end of
quote do .. block in
We can now replace the register page link
with code that shows the login and register links
only when the user isn’t signed in.
We have also used the
to show the logged in user’s email in the views.
The last thing we need to do now
is to add the ability for users to logout.
To do this,
we can add the following
If you now click the logout link in the navbar, the user will be logged out correctly.
Automatic login on signup
One final thing we will do
before finishing up with this tutorial
is to log users in immediately after signup.
We will add the following line
This automatically logs in the user after signup. There are many more details that we need to add to turn this into a complete authentication system, but we have already set up the basics.
You might have noticed that we can still access login and register pages even when we’re logged in. This is not something we want in a proper authentication system.
We can easily do this using plugs, which I will hopefully cover in a future blog post.
- Phoenix app with authentication - this article was a very useful reference for how to go about implementing the authentication feature.
- Passport and Addict are two Phoenix authentication libraries. Reading their source code was another excellent reference
- Rewriting ElixirStream.com from Rails to Phoenix is another article that describes how to add authentication to a Phoenix app.