Ruby On Rails User SignUp Email Confirmation Tutorial
Learning rails is very easy these days with the wealth of amazing tutorials out there however in my search I couldn’t find a good tutorial on how to build an email confirmation system for user sign up (you know the type where you have to click on a link in an email to confirm that the email address is actually yours). After spending some time figuring out I thought it would be good to share it so you don’t have to waste any time integrating this must have functionality.
The first thing to do in order to setup email confirmation for a rails 4 app is to add an emailconfirmed column to the users table. For more complex apps it is usually recommended to use a state machine gem such as statemachine or transitions which allows the user to exist in multiple states (eg inactive, active, suspended etc) but for this example we will keep it simple and just use a boolean column in the users table which allows the user be inactive or active. We will also add a column to hold the unique confirmation token that we will use to verify the user. In order to do this use the generate command in the terminal inside your rails app root folder:
rails generate migration add_email_confirm_column_to_users email_confirmed:boolean confirm_token:string
This should create the following migration file (please note the addition of :default => false
which will need to be done manually):
class AddEmailConfirmColumnToUsers < ActiveRecord::Migration
def change
add_column :users, :email_confirmed, :boolean, :default => false
add_column :users, :confirm_token, :string
end
end
Run rake db:migrate
to migrate the changes to the users model.
Next on the list is to update the routes.rb file. There are a couple of ways to implement this and in this example I chose to keep all the actions inside the users controller . For insights into the other option which would be to create a new controller specifically for the task of confirming a users email please see the railscasts episode on implementing password reset functionality to your rails app. As we are staying in the users controller we have to make the email_confirmed action a member of the user resource like so:
resources :users do
member do
get :confirm_email
end
end
Now that that is setup we can move onto creating the email that will be sent to a new user when they register on your site. Rails has some powerful inbuilt email magic that abstracts the actual workings under the hood away from us mere mortals and at this stage all you need to know is that it works. First make the file controllers/mailers/user_mailer.rb and insert the following code:
class UserMailer < ActionMailer::Base
default :from => "me@mydomain.com"
def registration_confirmation(user)
@user = user
mail(:to => "#{user.name} <#{user.email}>", :subject => "Registration Confirmation")
end
I externalised the default sending address (you probably want to update this with your email address) from the registrationconfirmation(user) method so it applies to any other methods in the usermailer.rb file but you can also include it inside of the method if you like. In this file the user.name and user.email variables refer to the name and email columns in the user table which you probably have already previously setup but perhaps with different naming conventions. Change the variables to reflect your specific naming scheme.
After setting up the method I made a view for the email called views/user_mailer/registration_confirmation.text.erb
. You will probably also want to setup a html version of this file but we’ll just go with the text version for the purpose of this tutorial (again make sure to alter the variable name user.name to reflect your naming scheme).
Hi <%= @user.name %>,
Thanks for registering! To confirm your registration click the URL below.
<%= confirm_email_user_url(@user.confirm_token) %>
As you can see here there is a reference to a confirmtoken which we have not created yet so now is a good time to do that. In the models/user.rb create a private method called confirmationtoken with the following code:
private
def confirmation_token
if self.confirm_token.blank?
self.confirm_token = SecureRandom.urlsafe_base64.to_s
end
end
Here we use the inbuilt functionality to generate a random string and assign it to confirmtoken. This unique random string is then inserted into the email that is sent to the user and is used to identify which user to verify. While we’re at it we can create the callback that ensures the confirmtoken is generated and inserted into the users table when a new user is created. To do this simply add the following at the top of your models/user.rb file:
before_create :confirmation_token
With that all setup we can ensure the email is sent to a user when they create a new account. To do this add UserMailer.registration_confirmation(@user).deliver
to the create action in controllers/users_controller.rb. My full create action looks like this:
def create
@user = User.new(user_params)
if @user.save
UserMailer.registration_confirmation(@user).deliver
flash[:success] = "Please confirm your email address to continue"
redirect_to root_url
else
flash[:error] = "Ooooppss, something went wrong!"
render 'new'
end
end
Notice that I redirect them to the rooturl rather than signing them in after they create their account. In my controllers/sessionscontroller.rb file create action which I use to sign users in I added an extra nested if/else statement inside the initial if/else statement to check if the email_confirmed field is set to true or false to only allow a user to sign in if their email has been confirmed. If not they are redirected to the signin page again and asked to activate their account. The sessions controller create action now looks like this (though yours may look somewhat different depending on your previous work):
def create
user = User.find_by_email(params[:email].downcase)
if user && user.authenticate(params[:password])
if user.email_confirmed
sign_in user
redirect_back_or user
else
flash.now[:error] = 'Please activate your account by following the
instructions in the account confirmation email you received to proceed'
render 'new'
end
else
flash.now[:error] = 'Invalid email/password combination' # Not quite right!
render 'new'
end
end
If you want to find out how I built my sessions functionality check out the stellar Hartl Rails Tutorial. It’s free!
Finally we come to the last piece of the puzzle which is the confirmemail action in the controllers/userscontroller.rb file. This is the action that the email link looks for when clicked. It takes the random string in the confirmtoken field in the users table and uses it to identify which user to verify. If it finds a user that corresponds to the random string in the confirmtoken field it sets the emailconfirmed field to true, and clears the confirmtoken field to invalidate a subsequent click on the now expired link. My confirm_email action looks like this:
def confirm_email
user = User.find_by_confirm_token(params[:id])
if user
user.email_activate
flash[:success] = "Welcome to the Sample App! Your email has been confirmed.
Please sign in to continue."
redirect_to signin_url
else
flash[:error] = "Sorry. User does not exist"
redirect_to root_url
end
end
Ok so actually that wasn’t the last last thing as the more eagle eyed of you will have noticed. There is a call to a emailactivate method on the line user.emailactivate and this method doesn’t exist yet. I created it in the models/user.rb file and it looks like this (it contains all the logic for manipulating the model so this is why it goes in the model file and is then called from the controller):
def email_activate
self.email_confirmed = true
self.confirm_token = nil
save!(:validate => false)
end
The (:validate => false)
protects against the unwanted password validation I have in my user model (I check to make sure that password exists and has a minimum length of 6 characters like so validates :password, length: { minimum: 6 }
). This was not covered in this tutorial but I thought I would leave it in as most of you will have similar functionality and it throws an error if the validation isn’t bypassed.
So now you should have a fully functional email confirmation working on your rails app. Go ahead, make a new user and test it to make sure it’s working.
Written by Rory Koehler
Related protips
4 Responses
Super cool and well written artice. I found it so helpfun bcos i dont like using devise to avoid complexity.
Cheers
Thank you for this concise and informative tutorial.
But actually I have an error when I trying to access to this function: "confirmemailuserurl"
And this is the error with Rails:
undefined method `confirmemailuserurl'
Can someone help me, it will be very usefull for me
Thanks in advance,
Thanks Rory, I seem to have it set up correctly and receive a 200 after sending the request, but I never receive an email. Any idea why?
@mdelrosa what do your logs say? Have you setup your email sending service (smtp)? See this tutorial for a guide on the various ways to do that https://launchschool.com/blog/handling-emails-in-rails