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:
1 2 | rails generate scaffold User name:string password_digest:string rake db:migrate |
1. Edit app/models/user.rb such that it reads:
1 2 3 4 | 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:
1 | 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | <%= 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…
1 | rails generate controller Sessions new create destroy |
Edit: app/controllers/sessions_controller.rb
Should look like this following edits:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | < 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:
1 2 3 4 5 | get "sessions/new" get "sessions/create" get "sessions/destroy" |
add:
1 2 3 4 5 | 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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:
1 | skip_before_filter :authorize |
For particular methods:
1 | skip_before_filter :authorize , only: [ :create , :update , :destroy ] |
8. Logoff code:
Add the following on pages where you want a logoff button:
1 2 3 4 5 6 | <% if session[ :user_id ] %> <%= button_to 'Logout' , logout_path, method: :delete %> <% end %> </div> <div id= "main" > <%= yield %> |
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