In this article we teach you how to build a simple authentication system for your Rails 3.1 (and above) application.

Initial Setup

First, we need to include the bcrypt ruby gem. Open your gemfile and find the line that says:

# gem 'bcrypt-ruby', '~> 3.0.0'

Uncomment the line. Next, run a bundler to install the gem:

bundle

This will install the BCrypt Ruby Gem, which will allow us to utilize hashed passwords.

Creating a User Model

We need to add a user model that we can authenticate against. Create a user model:

rails g model user email:string password_digest:string

Then run a rake db:migrate to update the database.

rake db:migrate

The password_digest field stores a salted hash of the user's password.

Now, before we dig in, lets create a a sample page that we want to protect with a username and password. Run the following command:

rails g controller Home show

This will create a controller called home, with an action called show. Next, open your routes file, remove the line from your routes file:

get "home/show"

Now, add the following lines:

resource :home, only: [:show]
root to: "home#show"

Our page to be protected is now created. Later, we will add the code to keep unauthenticated users from accessing this page.

Updating the User Model

Now that the database is squared away and we have a page to protect, we need to add a couple of lines of code to the model. Open your model. You will see something that looks similar to the following:

attr_accessible :email, :password_digest

Append the following code to the end of the line:

, :password, :password_confirmation

So that it looks something like this:

attr_accessible :email, :password_digest, :password, :password_confirmation

Next, add the following 2 lines of code.

has_secure_password
validates_presence_of :password, :on => :create
validates :email, uniqueness: true, presence: true

The first line tells Rails that we want to store and use a secure password. A few validations as well as a few methods are added. The second line adds a validation that checks for the presence of the password. The final line ensures that the email address specified is unique. Note that we don't actually check the formatting of the email address here, but you probably should. Validations will be covered in a later article.

One final thing, delete the public/index.html file so that Rails will take you to the correct site root.

Login Form

Now we need to build a form that will allow the user to log in. Lets create a controller called sessions to do this:

rails g controller Sessions new create destroy

Next, open your routes.rb file and remove the following lines:

get "sessions/new"
get "sessions/create"
get "sessions/destroy"

Now add the following line:

resources :sessions, only: [:new, :create, :destroy]

Now open the sessions/new.html.erb view and add the following code for the login form.

<h1>Log in</h1>
<p><%= flash[:notice] %></p>
<%= form_for :session, url: sessions_path do |f| %>
    <div class="field">
      <%= f.label :email %>
      <%= f.text_field :email %>
    </div>
    <div class="field">
      <%= f.label :password %>
      <%= f.password_field :password %>
    </div>
    <div class="actions"><%= f.submit :login %></div>
<% end %>

Next, we want to add some code to the controller to tell rails how to handle the form. Open the sessions_controller and add the following code for the create method:

Advertisement
user = User.find_by_email(params[:email])
if user && user.authenticate(params[:password])
  session[:user_id] = user.id
  redirect_to root_url, :notice => "Logged in!"
else
 flash[:notice] = "Invalid email or password"
 redirect_to new_session_path
end

Now we have a functional login form. Also, add the following code to the destroy method.

session[:user_id] = nil

This line of code will allow the user to log out.

Sign Up Form

Now, we need to create a sign up form where new users can sign up. First, lets create a new controller.

rails g controller users new create

This will create a Users controller that will allow users to sign up for an account. Next, open up your routes and remove the following lines:

get "users/new"
get "users/create"

Then, add the following line to make the controller resourceful.

resources :users, only: [:new, :create]

This will set up our resourceful routes so that they work properly. Now, open up the users controller and add the following code to the new method:

@user = User.new

Also add the following code to the create method:

@user = User.new(params[:user])
if @user.save
  redirect_to root_url, notice: "Signed up!"
else
  render :new
end

Now that we have all of the code required allow the user to sign up, we need to build the forms. First, add the following code to your users/new.html.erb view.

<h1>Sign Up</h1>

<%= form_for @user do |f| %>
  <% if @user.errors.any? %>
    <div class="error_messages">
      <h2>Form is invalid</h2>
      <ul>
        <% for message in @user.errors.full_messages %>
          <li><%= message %></li>
        <% end %>
      </ul>
    </div>
  <% end %>
  <div class="field">
    <%= f.label :email %>
    <%= f.text_field :email %>
  </div>
  <div class="field">
    <%= f.label :password %>
    <%= f.password_field :password %>
  </div>
  <div class="field">
    <%= f.label :password_confirmation %>
    <%= f.password_field :password_confirmation %>
  </div>
  <div class="actions"><%= f.submit "Sign Up" %></div>
<% end %>

Our signup form is now complete and you can access it from /users/new. Now it's time to Add some finishing touches.

Finishing Up

We are just about done, but first we need to add a few quick finishing touches. First, open your routes file and add the following routes:

get "login" => "sessions#new", :as => "login"
get "sign_up" => "users#new", :as => "sign_up"
get "logout" => "sessions#destroy", :as => "logout"

The first route will map /login to your login page, the second route will map /sign_up to your signup page, and the last route will map /logout to your logout page.

Next, open the ApplicationController and add the following code:

def current_user
  @current_user ||= User.find(session[:user_id]) if session[:user_id]
end

def require_valid_user
  if session[:user_id] == nil
    redirect_to login_path
  end
end

The first method will return the current user if the user is logged in. The second method gets called by a page that needs a valid user. Finally, add the following code to your home controller:

before_filter :require_valid_user

This line causes the application to prompt the user to log in if he/she attempts to access any action on the home controller. That's it! You now have a working authentication system in Rails 3.1 and above.