At present, newly registered users immediately have full access to their accounts. In this section, we’ll implement an account activation step to verify that the user controls the email address they used to sign up. This will involve associating an activation token and digest with a user, sending the user an email with a link including the token, and activating the user upon clicking the link.
Our strategy for handling account activation parallels user login and especially remembering users. The basic sequence appears as follows:
1. Start users in an “unactivated” state.
2. When a user signs up, generate an activation token and corresponding activation digest.
3. Save the activation digest to the database, and then send an email to the user with a link containing the activation token and user's email address.
4. When the user clicks the link, find the user by email address, and then authenticate the token by comparing with the activation digest.
5. If the user is authenticated, change the status from "unactivated" to "activated".
Account activations resource
As with sessions, we’ll model account activations as a resource even though they won’t be associated with an model. Instead, we’ll include the relevant data in the User model. Nevertheless, we’ll interact with account activations via a standard REST URL; because the activation link will be modifying the user’s activation status, we’ll plan to use the update action.
config/routes.js
Although we won’t use it in this tutorial, we’ll record the time and date of the activation in case we want it for future reference.
db/migrate/[timestamp]-add_activation_to_users.js
app/models/user.js
We then apply the migration as usual
Because every newly signed-up user will require activation, we should assign an activation token and digest to each user object before it’s created. We saw a similar idea in “User validations” Section, where we needed to convert an email address to lower-case before saving a user to the database.
app/models/user.js
Before moving on, we should also update our seed data
db/seeds.js
To apply the changes, reset the database to reseed the data as usual
Account activation mailer method
With the data modeling complete, we’re now ready to add the code needed to send an account activation email. To send email, we’ll use Mailjet
app/helpers/mailer_helper.js
The account activation text view.
app/views/user_mailer/account_activation.text
app/views/user_mailer/account_activation.html
To use the mailer in our application, we just need to add a couple of lines to the create action used to sign users up
app/controllers/users_controller.js
public/controllers/users_controller.js
Because redirects to the root URL instead of to the profile page and doesn’t log the user in as before, the test suite is currently failing, even though the application is working as designed. We’ll fix this by editing the test suite.
With the code as above, the test should be successful
Activating the account
Now that we have a correctly generated email, we need to write an update action in the Account Activations controller to activate the user. Following the model of passwords and remember tokens, we plan to find and authenticate the user with code something like this
The above code uses the authenticated method to test if the account activation digest matches the given token, but at present this won’t work because that method is specialized to the remember token. We can generalize the method by adding a function argument with the name of the digest, and then use string interpolation
app/models/user.js
At this point, the tests should be failing. The reason for the failure is that the current_user method and the test for null digests both use the old version of authenticated, which expects one argument instead of two. To fix this, we simply update the two cases to use the generalized method
app/helpers/sessions_helper.js
test/models/user_test.js
At this point, the tests should be successful
With the authenticated method, we’re now ready to write an update action that authenticates the user corresponding to the email address in the params hash
app/controllers/account_activations_controller.js
public/app.js
You should now be able to click in the URL from the email to activate the relevant user.
Of course, currently user activation doesn’t actually do anything, because we haven’t changed how users log in. In order to have account activation mean something, we need to allow users to log in only if they are activated.
app/controllers/sessions_controller.js
public/controllers/sessions_controller.js
Activation test and refactoring
In this section, we’ll add an integration test for account activation.
At this point, the test suite should be successful
With the test, we’re ready to refactor a little by moving some of the user manipulation out of the controller and into the model. In particular, we’ll make an activate method to update the user’s activation attributes and a send_activation_email to send the activation email.
app/models/user.js
app/controllers/account_activations_controller.js
app/controllers/users_controller.js
These are exactly the kinds of details that are easy to miss during even a simple refactoring but will be caught by a good test suite. Speaking of which, the test suite should still be successful