Last Updated: September 29, 2021
·
52.29K
· rorykoehler

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.

4 Responses
Add your response

Super cool and well written artice. I found it so helpfun bcos i dont like using devise to avoid complexity.
Cheers

over 1 year ago ·

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 `confirm
emailuserurl'
Can someone help me, it will be very usefull for me
Thanks in advance,

over 1 year ago ·

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?

over 1 year ago ·

@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

over 1 year ago ·