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.
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 %>