Rails Authentication with Authlogic

Share this article

Today, I’d like introduce you to AuthLogic, a simple authentication solution for Ruby built by Ben Johnson.

AuthLogic is unobtrusive and pretty low-level. It does not rely on generated code (like Devise, for example) – instead it provides only the tools that you can use to code your app the way you like. In a sense, AuthLogic is similar to Sorcery.

Let’s build a Rails app to allow users to register, sign in,and out, as well as reset their password. While doing this, we will discuss AuthLogic’s various features and settings. After reading this tutorial you will be ready to use AuthLogic in your real projects.

Source code can be found on GitHub.

Demo app is available at sitepoint-authlogic.herokuapp.com.

Getting Started

The demo app that I am going to show you is built with Rails 4.1, but AuthLogic supports Rails 3, and even Rails 2 (there is a separate branch for it).

Create a new “Logical” app without the default testing suite:

$ rails new Logical -T

Drop in the following gems:

Gemfile

[...]
gem 'bootstrap-sass'
gem 'authlogic', '3.4.6'
[...]

and run

$ bundle install

Take advantage of Bootstrap’s styles if you wish:

stylesheets/application.scss

@import 'bootstrap-sprockets';
@import 'bootstrap';

and modify the layout:

views/layouts/application.html.erb

[...]
<nav class="navbar navbar-inverse">
  <div class="container">
    <div class="navbar-header">
      <%= link_to 'Logical', root_path, class: 'navbar-brand' %>
    </div>
    <div id="navbar">
      <ul class="nav navbar-nav">
        <li><%= link_to 'Home', root_path %></li>
      </ul>
    </div>
  </div>
</nav>

<div class="container">
  <% flash.each do |key, value| %>
    <div class="alert alert-<%= key %>">
      <%= value %>
    </div>
  <% end %>

  <%= yield %>
</div>
[...]

Now create the controller for static pages:

pages_controller.rb

class PagesController < ApplicationController
  def index
  end
end

Then the corresponding view:

views/pages/index.html.erb

<div class="jumbotron">
<div class="container">
  <h1>Welcome!</h1>
  <p>Sign up to get started.</p>
  <p>
    <%= link_to '#', class: 'btn btn-primary btn-lg' do %>
      Sign Up &raquo;
    <% end %>
  </p>
</div>

and route:

config/routes.rb

[...]
root to: 'pages#index'
[...]

When you are done, proceed to the next step and let’s integrate AuthLogic together!

Setting Up AuthLogic

Models

Using AuthLogic involves two models: a basic one that we are going to call User and a “special” one, inherited from Authlogic::Session::Base that will be called UserSession (you can read this section to get a quick idea of how this solution works).

First of all, create a new migration:

$ rails g model User email:string crypted_password:string password_salt:string persistence_token:string

(Here is an example migration with all possible fields)

Add the following line to the generated migration:

migrations/xxx_create_users.rb

[...]
add_index :users, :email, unique: true
[...]

Apply the migration:

$ rake db:migrate

The email, crypted_password, and password_salt fields are actually optional. AuthLogic does not really care how you are authenticate – use LDAP or OAuth 2, for example. Here is the list of AuthLogic “add-ons” for supported authentication methods, however many of them are not actively maintained.

By the way, you could also have a login field that AuthLogic will use instead of email.

The persistence_token field is required. AuthLogic uses it to persist the user’s session in a secure way.

Now tweak the model:

models/user.rb

[...]
acts_as_authentic
[...]

This equips the User model with AuthLogic functionality. acts_as_authentic accepts a block to redefine default settings (like password validation rules, for example – we will discuss it in the last section of the article).

Now create a new, special model:

models/user_session.rb

class UserSession < Authlogic::Session::Base
end

Note for Windows Users

There is a pretty serious error that Windows users are likely to encounter when using AuthLogic with Ruby 2.1 or 2.2 (I have not tested with Ruby 1.9). Full discussion can be found on GitHub, but, in short, this error is related to SCrypt, a gem that implements secure password hashing algorithm. AuthLogic uses SCrypt as a default crypto provider, but on Windows it constantly returns segmentation error and the server crashes.

The quickest way is to use another provider – AuthLogic offers a handful of them.

In this demo I will stick to SHA512, so tweak the model:

models/user.rb

[...]
acts_as_authentic do |c|
    c.crypto_provider = Authlogic::CryptoProviders::Sha512
end
[...]

Controllers and Helpers

Let’s take care of the controller to manage users:

users_controller.rb

class UsersController < ApplicationController
  def new
    @user = User.new
  end

  def create
    @user = User.new(users_params)
    if @user.save
      flash[:success] = "Account registered!"
      redirect_to root_path
    else
      render :new
    end
  end

  private

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

This is a really basic controller that will be responsible for users’ registrations. Make sure to permit both password and password_confirmation attributes, because, by default, AuthLogic checks if those two match.

Here’s the controller to manage logging in and out:

user_sessions_controller.rb

class UserSessionsController < ApplicationController
  def new
    @user_session = UserSession.new
  end

  def create
    @user_session = UserSession.new(user_session_params)
    if @user_session.save
      flash[:success] = "Welcome back!"
      redirect_to root_path
    else
      render :new
    end
  end

  def destroy
    current_user_session.destroy
    flash[:success] = "Goodbye!"
    redirect_to root_path
  end

  private

  def user_session_params
    params.require(:user_session).permit(:email, :password, :remember_me)
  end
end

As you can see, we use the UserSession model to authenticate the user. It persists the user’s session automatically, so the controller is really clean and simple.

What about the current_user_session? It is a helper method:

application_controller.rb

[...]
private

def current_user_session
  return @current_user_session if defined?(@current_user_session)
  @current_user_session = UserSession.find
end

def current_user
  return @current_user if defined?(@current_user)
  @current_user = current_user_session && current_user_session.user
end

helper_method :current_user_session, :current_user
[...]

UserSession.find automatically uses persistence_token to find the current session.

I’ve also added current_user to easily fetch current user’s record based on the current session.

Routes

We have to set up some routes:

config/routes.rb

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

resources :user_sessions, only: [:create, :destroy]

delete '/sign_out', to: 'user_sessions#destroy', as: :sign_out
get '/sign_in', to: 'user_sessions#new', as: :sign_in
[...]

Views

Lastly, let’s deal with the views.

First of all, the layout:

views/layouts/application.html.erb

[...]
<nav class="navbar navbar-inverse">
  <div class="container">
    <div class="navbar-header">
      <%= link_to 'Logical', root_path, class: 'navbar-brand' %>
    </div>
    <div id="navbar">
      <ul class="nav navbar-nav">
        <li><%= link_to 'Home', root_path %></li>
      </ul>
      <ul class="nav navbar-nav pull-right">
        <% if current_user %>
          <li><span><%= current_user.email %></span></li>
          <li><%= link_to 'Sign Out', sign_out_path, method: :delete %></li>
        <% else %>
          <li><%= link_to 'Sign In', sign_in_path %></li>
        <% end %>
      </ul>
    </div>
  </div>
</nav>
[...]

Here, I simply populate the top menu.

Now the “Register” page:

views/users/new.html.erb

<div class="page-header"><h1>Register</h1></div>

<%= form_for @user do |f| %>
  <%= render 'shared/errors', object: @user %>

  <div class="form-group">
    <%= f.label :email %>
    <%= f.email_field :email, class: 'form-control' %>
  </div>

  <div class="form-group">
    <%= f.label :password %>
    <%= f.password_field :password, class: 'form-control' %>
  </div>

  <div class="form-group">
    <%= f.label :password_confirmation %>
    <%= f.password_field :password_confirmation, class: 'form-control' %>
  </div>

  <%= f.submit 'Register', class: 'btn btn-primary btn-lg' %>
<% end %>

AuthLogic automatically validates that the e-mail is valid and that the passwords match and are at least 4 characters long. You can redefine those settings easily inside the model/user.rb – we will discuss that in a bit.

Add the shared partial to display errors:

views/shared/_errors.html.erb

<% if object.errors.any? %>
  <div class="panel panel-warning errors">
    <div class="panel-heading">
      <h5><i class="glyphicon glyphicon-exclamation-sign"></i> Found errors while saving</h5>
    </div>

    <ul class="panel-body">
      <% object.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
    </ul>
  </div>
<% end %>

Don’t forget to add a link to the “Register” page inside index.html.erb:

views/pages/index.html.erb

<div class="jumbotron">
  <div class="container">
    <h1>Welcome!</h1>
    <p>Sign up to get started.</p>
    <p>
      <%= link_to new_user_path, class: 'btn btn-primary btn-lg' do %>
        Sign Up &raquo;
      <% end %>
    </p>
  </div>
</div>

And, lastly, code for the “Sign In” view:

views/user_sessions/new.html.erb

<div class="page-header"><h1>Sign In</h1></div>

<%= form_for @user_session do |f| %>
  <%= render 'shared/errors', object: @user_session %>

  <div class="form-group">
    <%= f.label :email %>
    <%= f.email_field :email, class: 'form-control' %>
  </div>

  <div class="form-group">
    <%= f.label :password %>
    <%= f.password_field :password, class: 'form-control' %>
  </div>

  <div class="form-group">
    <%= f.label :remember_me %>
    <%= f.check_box :remember_me %>
  </div>

  <%= f.submit "Log in!", class: 'btn btn-primary btn-lg' %>
<% end %>

At this point you are good to go – boot up the server and register your first user!

Storing Additional Info

AuthLogic supports a handful of “magic attributes” that are populated automatically. You can use them to store additional information about a user, like last login date or last used IP.

Go ahead and create a new migration:

$ rails g migration add_magic_columns_to_users

Tweak it like so:

xxx_add_magic_columns_to_users.rb

class AddMagicColumnsToUsers < ActiveRecord::Migration
  def change
    add_column :users, :login_count, :integer, :null => false, :default => 0
    add_column :users, :failed_login_count, :integer, :null => false, :default => 0
    add_column :users, :last_request_at, :datetime
    add_column :users, :current_login_at, :datetime
    add_column :users, :last_login_at, :datetime
    add_column :users, :current_login_ip, :string
    add_column :users, :last_login_ip, :string
  end
end

and run the migration:

$ rake db:migrate

Now, for simplicity, let’s display all this information on the main page if the user is logged in:

views/pages/index.html.erb

<% if current_user %>
  <div class="page-header"><h1>Welcome back!</h1></div>

  <h2>Some info about you...</h2>

  <div class="well well-lg">
    <ul>
      <li>Login count: <%= current_user.login_count %></li>
      <li>Failed login count: <%= current_user.failed_login_count %></li>
      <li>Last request: <%= current_user.last_request_at %></li>
      <li>Current login at: <%= current_user.current_login_at %></li>
      <li>Last login at: <%= current_user.last_login_at %></li>
      <li>Current login IP: <%= current_user.current_login_ip %></li>
      <li>Last login IP: <%= current_user.last_login_ip %></li>
    </ul>
  </div>
<% else %>
    [...]
<% end %>

Log in and observe the result. This may come in handy if you wish to check, for example, how often your users visit a website.

Resetting a Password

Users tend to forget their passwords, therefore it is crucial to present them with a way to reset it. AuthLogic provides you with a tool to add this functionality, as well.

The author of AuthLogic suggests using a simple mechanism where a user first enters an e-mail, then receives a link to update the password, and then follows the link to actually set the new password. This link contains a special “perishable” token that has to be reset.

Therefore we need to add a new field called perishable_token to the users table. Note that we do not call it something like reset_password_token. AuthLogic does not dictate that this token can be used only for password resetting – you may use it, for example, to activate users’ accounts.

Apart from perishable_token, AuthLogic also supports single_access_token that is ideal for APIs – it provides access, but does not persist. Read more here.

Okay, so create and apply a new migration:

$ rails g migration add_perishable_token_to_users perishable_token:string
$ rake db:migrate

Obviously we need a special controller to manage password resets and a mailer.

Start with controller:

password_resets_controller.rb

class PasswordResetsController < ApplicationController
  def new
  end

  def create
    @user = User.find_by_email(params[:email])
    if @user
      @user.deliver_password_reset_instructions!
      flash[:success] = "Instructions to reset your password have been emailed to you."
      redirect_to root_path
    else
      flash[:warning] = "No user was found with that email address"
      render :new
    end
  end

  def edit
    @user = User.find_by(perishable_token: params[:id])
  end

  def update
    @user = User.find_by(perishable_token: params[:id])
    if @user.update_attributes(password_reset_params)
      flash[:success] = "Password successfully updated!"
      redirect_to root_path
    else
      render :edit
    end
  end

  private

  def password_reset_params
    params.require(:user).permit(:password, :password_confirmation)
  end
end

new is the action that will be called when a user clicks on the “Forgot your password?” link.

create processes the sent data and tries to fetch a user by e-mail. If this user is found, password reset instructions are sent to their e-mail.

edit is called when a user visits the link that was sent to the provided e-mail. This link contains our perishable token that is employed to find the user record. The edit page contains another form to enter a new password.

Inside the update action, fetch the user and update their password.

We’ll need the routes, as well:

config/routes.rb

...
resources :password_resets, only: [:new, :create, :edit, :update]
...

Now, add a new method to your model:

models/user.rb

[...]
def deliver_password_reset_instructions!
  reset_perishable_token!
  PasswordResetMailer.reset_email(self).deliver_now
end
[...]

reset_perishable_token! is a method supplied by AuthLogic – it simply sets perishable_token to a new random value and saves the record.

We also have to create our new mailer:

mailers/application_mailer.rb

class ApplicationMailer < ActionMailer::Base
  default from: "from@example.com"
  layout 'mailer'
end

mailers/password_reset_mailer.rb

class PasswordResetMailer < ApplicationMailer
  def reset_email(user)
    @user = user
    mail(to: @user.email, subject: 'Password reset instructions')
  end
end

views/layouts/mailer.text.erb

<%= yield %>

views/password_reset_mailer/reset_email.text.erb

Here is your link to reset password: <%= edit_password_reset_url(@user.perishable_token) %>.

Also don’t forget to set the default URL options:

config/environments/development.rb

config.action_mailer.default_url_options = { host: '127.0.0.1:3000' }

Please note that in the development environment, e-mails won’t actually be sent, but you’ll be able to see their contents inside the console. Also note that my demo app won’t send e-mails, but it’s easy to setup for a production environment. Read more here.

Lastly, update the views to include the “Forgot your password?” link:

views/user_sessions/new.html.erb

<div class="page-header"><h1>Sign In</h1></div>

<%= form_for @user_session do |f| %>
  [...]
  <%= f.submit "Log in!", class: 'btn btn-primary btn-lg' %>
  <br/><br/>
  <%= link_to 'Forgot your password?', new_password_reset_path, class: 'btn btn-sm btn-default' %>
<% end %>

Now go ahead and check how this is working!

Tweaking the Default Settings

As I already told you, AuthLogic provides some default settings that you can easily change by passing a block to acts_as_authentic. To learn more about those settings, you can browse the documentation, but let me highlight some of them.

Brute Force

By default, AuthLogic tries to prevent brute force attacks (when someone is using a program to “guess” a password simply by using various combinations of symbols), but only if the failed_login_count field is present in your table. There are two settings that you can change:

  • consecutive_failed_logins_limit (default is 50) – allowed number of consecutive failed logins. Set to 0 to disable brute force protection.
  • failed_login_ban_for (default is 2 hours) – how long the user’s account will be locked out. Set to 0 to lock out permanently.

More can be read here.

HTTP Basic Auth

AuthLogic supports HTTP basic authentication, which is enabled by default.

Password Configuration

There are various settings that can be used to change the default fields to store login and password information, as well as changing the method to find the user’s session.

There is also a page that lists settings related to password’s validation rules and password confirmation.

Logout on Timeout

You can also instruct AuthLogic to mark the user as logged out after a certain period of time. Just set logout_on_timeout to true and use stale? to check if the user needs to log back in.

Read more here.

You can also use the logged_in_timeout setting to determine when a user is logged in or not. More can be found here.

E-mail Configuration

Visit this page to learn about various options related to e-mails (field to store e-mail, validation rules, and more).

There are many more options that you can use for AuthLogic customization, so be sure to browse the documentation.

Conclusion

In this article, we’ve taken a look at AuthLogic and built a simple demo app. It is really interesting to observe the various authentication solutions and compare their philosophies, isn’t it?

Which authentication solution do you prefer and why? Have you encountered any limitations or special cases that required you to develop your own authentication system? Share your experience!

Feedback is welcome, as always. Thanks for staying with me and see you soon.

Frequently Asked Questions (FAQs) about Rails Authentication with Authlogic

How do I install Authlogic in my Rails application?

To install Authlogic in your Rails application, you first need to add the gem to your Gemfile. Open your Gemfile and add the following line: gem 'authlogic'. Then, run bundle install to install the gem. After the gem is installed, you can use it in your application by including it in your User model with acts_as_authentic.

How do I create a new session in Authlogic?

Creating a new session in Authlogic is straightforward. First, you need to create a new Session model. This can be done by running rails generate model UserSession. Then, in your controller, you can create a new session by calling UserSession.new(params[:user_session]). This will create a new session with the parameters passed from the form.

How do I authenticate a user in Authlogic?

Authenticating a user in Authlogic is done by calling the save method on a UserSession instance. If the session is valid, the user will be logged in and the method will return true. If the session is not valid, the method will return false and the user will not be logged in.

How do I log out a user in Authlogic?

Logging out a user in Authlogic is done by calling the destroy method on the current UserSession instance. This will end the session and log out the user.

How do I check if a user is logged in Authlogic?

You can check if a user is logged in Authlogic by calling the UserSession.find method. If a session is found, it means that a user is logged in. If no session is found, it means that no user is logged in.

How do I handle password encryption in Authlogic?

Authlogic handles password encryption automatically. When you call acts_as_authentic in your User model, Authlogic will automatically encrypt the password using a secure hash function. You don’t need to worry about handling password encryption yourself.

How do I customize the validation messages in Authlogic?

You can customize the validation messages in Authlogic by overriding the default error messages in your User model. For example, you can change the message for a blank password by adding validates_presence_of :password, message: 'Your custom message' to your User model.

How do I use Authlogic with Rails API?

You can use Authlogic with Rails API by including the Authlogic module in your ApplicationController and calling acts_as_authentic in your User model. Then, you can use the UserSession model to handle authentication in your API endpoints.

How do I handle session timeout in Authlogic?

Authlogic provides a logout_on_timeout configuration option that you can use to automatically log out users after a certain period of inactivity. You can set this option in your UserSession model.

How do I handle multiple sessions in Authlogic?

Authlogic does not support multiple sessions out of the box. However, you can implement this feature yourself by creating a separate Session model for each type of session you want to support. Then, you can use these models to handle authentication for different types of users.

Ilya Bodrov-KrukowskiIlya Bodrov-Krukowski
View Author

Ilya Bodrov is personal IT teacher, a senior engineer working at Campaigner LLC, author and teaching assistant at Sitepoint and lecturer at Moscow Aviations Institute. His primary programming languages are Ruby (with Rails) and JavaScript. He enjoys coding, teaching people and learning new things. Ilya also has some Cisco and Microsoft certificates and was working as a tutor in an educational center for a couple of years. In his free time he tweets, writes posts for his website, participates in OpenSource projects, goes in for sports and plays music.

GlennG
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week