Adding User Authentication to a Ruby on the Rails Application

Adding User authentication to a Ruby on the rails app. This follows the method discussed in “Agile Web Development with Rails”. Some of the example code is copied from there.

0. Create user database:

rails generate scaffold User name:string password_digest:string
rake db:migrate

1. Edit app/models/user.rb such that it reads:

class User < ActiveRecord::Base
  validates :name, presence: true, uniqueness: true
  has_secure_password # This makes the password a digest and forces you to enter it twice.
end

2. add bcrypt to the gemfile, edit Gemfile:

gem 'bcrypt-ruby'

You may need to run “bundle install”.

3. Edit the user edit form to add password confirmation:

app/views/users/_form.html.erb so that it reads as follows:

<%= form_for(@user) do |f| %>
  <% if @user.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@user.errors.count, "error") %> prohibited this user from being saved:</h2>

      <ul>
      <% @user.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <fieldset>

  <div class="field">
    <%= f.label :name %><br />
    <%= f.text_field :name %>
  </div>
  <div class="field">
    <%= f.label :password, 'Password' %><br />
    <%= f.password_field :password %>
  </div>

  <div>
    <%= f.label :password_confirmation, 'Confirm' %>:
    <%= f.password_field :password_confirmation %>
  </div>

  <div class="actions">
    <%= f.submit %>
  </div>

  </fieldset>
<% end %>

4. Create session controller…

rails generate controller Sessions new create destroy

Edit: app/controllers/sessions_controller.rb

Should look like this following edits:

class SessionsController < ApplicationController
  skip_before_filter :authorize

  def new
  end

  def create
    user = User.find_by_name(params[:name])
    if user and user.authenticate(params[:password])
      session[:user_id] = user.id
      #redirect_to admin_url # use this to redirect the user somewhere after login
    else
      redirect_to login_url, alert: "Invalid user/password combination"
    end
  end

  def destroy
    session[:user_id] = nil
  end

end

You should open localhost:3000/users at this point and create a user. We’ll be adding authentication next and you will need at least one user to be able to login and create more.

5. Create the login page.

Your ads will be inserted here by

Easy AdSense Pro.

Please go to the plugin admin page to paste your ad code.

Edit app/views/sessions/new.html.erb to read:

<div class="depot_form" >
<% if flash[:alert] %>
<p id="notice" ><%= flash[:alert] %></p>
<% end %>

<%= form_tag do %>
<fieldset>
<legend>Please Log In</legend>

<div>
<label for="name" >Name:</label>
<%= text_field_tag :name, params[:name] %>
</div>

<div>
<label for="password" >Password:</label>
<%= password_field_tag :password, params[:password] %>
</div>

<div>
<%= submit_tag "Login" %>
</div>
</fieldset>
<% end %>
</div>

Edit the route controller:

config/routes.rb remove:

  get "sessions/new"

  get "sessions/create"

  get "sessions/destroy"

add:

  controller :sessions do
    get 'login' => :new
    post 'login' => :create
    delete 'logout' => :destroy
  end

6. Now we need to add a “filter” to provide for authenication:

edit app/controllers/application_controller.rb

It should read as follows:

class ApplicationController < ActionController::Base
  protect_from_forgery

  before_filter :authorize

  # ...

  protected

  def authorize
    unless User.find_by_id(session[:user_id])
      redirect_to login_url, notice: "Please log in"
    end
  end
end

7. You’ll need to skip authorisation for all controllers that don’t need a login.

The following with skip authorisation for all methods in a controller add the following after the beginning of the class definiton:

skip_before_filter :authorize

For particular methods:

skip_before_filter :authorize, only: [:create, :update, :destroy]

8. Logoff code:

Add the following on pages where you want a logoff button:

<% if session[:user_id] %>
<%= button_to 'Logout', logout_path, method: :delete %>
<% end %>
</div>
<div id="main" >
<%= yield %>
  • Amanda Abelove

    Great tutorial! To make it work… users controller needs to say:

    # Never trust parameters from the scary internet, only allow the white list through.
    def user_params
    params.require(:user).permit(:name, :password, :password_confirmation)
    end
    end