In Ruby On Rails By Leigh Halliday January 03, 2015 Leigh Halliday

User Authentication in Rails

Our Goal

In this article I'd like to discuss how to implement user authentication into this blog. In this article about modelling the data for our blog we talked about wanting to keep track of Authors, and that they would be stored in the User model. This is the user that will need to authenticate into the system.

By having the user authenticate, we can give them access to the admin section of the website, which has security and permissions attached to it.

User Authentication in Rails

There are a number of ways you can include authentication on a website. You can opt for basic http auth which is quite easy to implement in Ruby on Rails, but for this I wanted to have the authentication tied to a User account in the system, with a proper login form.

The two most common gems for handling user authentication are Devise and Sorcery. Both are more than capable, supported by the community, and offer an easy way to login using external services (Facebook, Twitter), but they are fairly different when it comes to ideology.

Devise acts as a Rails Engine, which already has controllers, actions, and views, which you can override if you want to, or you can use them with the default functionality that they come with.

Sorcery is much more light-weight, and provides you with a number of helper methods that you can use, but you're on your own in terms of setting up controllers, actions, and views for handling logging in and logging out of the website.

I decided to choose Sorcery for this project, mostly because I have worked with Devise before on others and wanted to try it out. In the end I much preferred having the freedom that Sorcery provided, able to create my own controllers rather than having to figure out what the correct way was to override the one that came in Devise.

Setting Up Rails for Sorcery

You will first want to add Sorcery to your Gemfile.

gem 'sorcery'

After installing Sorcery you can run this command, which will create a User model for you along with some DB migrations.

rails generate sorcery:install remember_me reset_password

Because I already had a users table, I had to modify the migrations that Sorcery created, which ended up looking something like this:

class SorceryRememberMe < ActiveRecord::Migration
  def change
    add_column :users, :crypted_password, :string, :null => false
    add_column :users, :salt, :string, :null => false
    add_column :users, :remember_me_token, :string, :default => nil
    add_column :users, :remember_me_token_expires_at, :datetime, :default => nil
    add_column :users, :reset_password_token, :string, :default => nil
    add_column :users, :reset_password_token_expires_at, :datetime, :default => nil
    add_column :users, :reset_password_email_sent_at, :datetime, :default => nil

    add_index :users, :remember_me_token
    add_index :users, :reset_password_token
  end
end

Ensure that you have this line of code in your User model: authenticates_with_sorcery!

Logging In and Out

I then created some routes for logging in and out of the system.

get 'login',  to: 'user_sessions#new',     as: :login
post 'login', to: 'user_sessions#create'
get 'logout', to: 'user_sessions#destroy', as: :logout

These routes point to the controller that is below. This controller uses a couple helper methods that Sorcery provides, such as login and logout. Other than that it is just normal Rails code.

class UserSessionsController < ApplicationController

  def new
    @user = User.new
  end

  def create
    if @user = login(user_params[:email], user_params[:password])
      redirect_back_or_to(admin_root_path, notice: 'Login successful')
    else
      @user = User.new
      flash.now[:alert] = 'Login failed'
      render action: 'new'
    end
  end

  def destroy
    logout
    redirect_to root_path, notice: "You've been logged out"
  end

  private

  def user_params
    params.require(:user).permit(:email, :password)
  end
end

Requiring Auth and Checking If Authenticated

Lastly, Sorcery provides a before action filter which you can put in your controllers that require authentication to access. This can take the form of the following code below:

class Admin::BaseController < ApplicationController
  before_filter :require_login

  private

  def not_authenticated
    redirect_to login_path, alert: "Please login first"
  end
end

The not_authenticated method will be called if the user is not yet authenticated. To check whether there is currently an authenticated user, you can call logged_in? (available in the view) which will return a Boolean, or current_user (available in view) which will return the current User or nil if they aren't authenticated.

Not Yet Implemented

I haven't yet implemented the forgotten password functionality nor the remember me functionality, purely because this is for my own blog and I haven't required it yet :) I already have the required DB migrations in place, so it won't be much work to do when I get around to it (I hope!).