🤯 50% Off! 700+ courses, assessments, and books

Simple Rails Authentication with Clearance

Ilya Bodrov-Krukowski
Share

Screenshot 2015-09-16 13.04.04

During the past month I have covered various authentication solutions for Rails. This article is the fifth in the series (oh my) and our guest today is Clearance.

Clearance is an opinionated e-mail/password-based authentication and authorization solution for Rails created by Thoughtbot Inc (the guys who created FactoryGirl, PaperClip and other cool libraries). Clearance is designed as a Rails engine. It is intended to be small, simple, and well-tested. In a sense, it is similar to Devise in that Clearance has many defaults to help you get started in nearly five minutes, and all those defaults can be easily overridden. In contrast to Devise, however, Clearance presents only minimal functionality that can be customized further as you see fit.

This solution is actively maintained, so I really recommend trying it out. In this article I will demonstrate how to integrate Clearance, restrict access, customize it, and use additional checks during sign-in.

Source code can be found on GitHub.

Working demo is available at sitepoint-clearance.herokuapp.com.

Preparations

To start off, create a new Rails app under a codename “Clear Sky”:

$ rails new ClearSky -T

For this demo Rails 4.2 will be used, but Clearance is compatible with Rails 3.2, as well.

If you wish to follow along with how I style the app, add the following gem to your Gemfile to take advantage of Bootstrap’s styles:

Gemfile

[...]
gem 'bootstrap-sass'
[...]

Also modify application.scss file:

stylesheets/application.scss

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

Now update the layout. You don’t have to add those Bootstrap-specific blocks, but make sure that flash messages are being rendered somewhere because Clearance relies on them to display important information to the user.

layouts/application.html.erb

[...
<nav class="navbar navbar-inverse">
  <div class="container">
    <div class="navbar-header">
      <%= link_to 'Clear Sky', root_path, class: 'navbar-brand' %>
    </div>
    <div id="navbar">

    </div>
  </div>
</nav>

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

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

We will also need a basic root page, so create a pages controller and the corresponding route:

pages_controller.rb

class PagesController < ApplicationController
  def index
  end
end

config/routes.rb

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

views/pages/index.html.erb

<div class="page-header"><h1>Welcome!</h1></div>

Now everything is ready to add Clearance to our project. Proceed to the next step!

Integrating Clearance

Getting started with Clearance is very easy. First of all drop the gem into your Gemfile:

Gemfile

[...]
gem 'clearance', '~> 1.11'
[...]

Now, run a special generator and apply migrations:

$ rails generate clearance:install
$ rake db:migrate

This generator does the following:

  • Creates a new clearance.rb file inside the initializers directory.
  • Creates a new User model, migration file, and adds the line include Clearance::User to it. This model will have the following attributes:
    • email (string)
    • encrypted_password (string)
    • confirmation_token (string)
    • remember_token (string)
    • Basic attributes (id, created_at, updated_at)
  • Adds include Clearance::Controller into the ApplicationController. This way the controllers will have special Clearance methods.

If you already have a User model, it will be modified accordingly. If you don’t want to call your model User, modify the config.user_model setting inside the clearance.rb initializer file. All in all, this file is the first place to tweak Clearance’s settings – for the full list of them refer to this section in the docs.

Note that even though confirmation_token is present, it is not being used. Creating a user simply means adding a new record to the users table and signing that user into the app. So, if you want to ensure that e-mail is confirmed before the account can be used, additional steps need to be taken. In the following sections we will discuss how to customize Clearance and perform additional sign-in checks in a more detail.

After running Clearance’s generator, a small todo list will be printed out in the console, so make sure you have completed all the required steps. If you are following along, only one additional action has to be done:

config/environments/development.rb

[...]
config.action_mailer.default_url_options = { host: 'localhost:3000' }
[...]

For a production environment, add settings that work for you.

Note that you will probably want to modify the sender’s e-mail address, as well:

config/initializers/clearance.rb

[...]
config.mailer_sender = 'reply@example.com'
[...]

Clearance is now up and running, so we can add “Log In” and “Sign Out” menu items:

layouts/application.html.erb

[...]
<div id="navbar">
  <% if signed_in? %>
    <ul class="nav navbar-nav">
      <li><%= link_to 'Add Page', new_page_path %></li>
    </ul>
  <% end %>

  <ul class="nav navbar-nav pull-right">
    <% if signed_in? %>
      <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>
[...]

signed_in? and current_user are helper methods provided by Clearance that are pretty self-explanatory. You can also use the signed_out? method.

The sign_out_path and sign_in_path routes are also created by Clearance – we will discuss them a bit more in the next section.

Restricting Access

Now as we added the basic authentication system, you probably want to restrict access to some pages of the website. That is really easy to do. Suppose, we have a new action inside the PagesController:

pages_controller.rb

[...]
def new
end
[...]

config/routes.rb

[...]
resources :pages, only: [:new]
[...]

views/pages/new.html.erb

<div class="page-header"><h1>Add Page</h1></div>

To allow only authenticated users to access this page, simply add a before_action:

pages_controller.rb

before_action :require_login, only: [:new]

Now, if an unauthenticated user tries to access this route, they will be redirected to the “Log In” page. To change this behavior, override the url_after_denied_access_when_signed_out method in your ApplicationController and return a route of your choice. Read more here.

Clearance also provides routing constraints that can come in handy. For example, if I wanted to define a special root route for all authenticated users, I’d use the following constraints:

config/routes.rb

[...]
constraints Clearance::Constraints::SignedIn.new do
  root to: 'pages#new', as: :signed_in_root
end

constraints Clearance::Constraints::SignedOut.new do
  root to: 'pages#index'
end
[...]

Here, all authenticated users will have pages#new as the root page. For guests, pages#index will be the root. Please note that if you define two root routes, one of them has to be named, otherwise an error will be raised.

Customizing Clearance

You are probably wondering how to customize Clearance further, for example how to modify views, controllers’ actions or routes. Well, that’s pretty simple as well.

Let’s start with routes. Run the following command to copy the default routes into your routes.rb file:

$ rails generate clearance:routes

This command will also set the config.routes setting to false, meaning that custom routes will be used.

Inside the routes.rb file, you will see some new lines of code:

config/routes.rb

[...]
resources :passwords, controller: "clearance/passwords", only: [:create, :new]
resource :session, controller: "clearance/sessions", only: [:create]

resources :users, controller: "clearance/users", only: [:create] do
  resource :password,
           controller: "clearance/passwords",
           only: [:create, :edit, :update]
end

get "/sign_in" => "clearance/sessions#new", as: "sign_in"
delete "/sign_out" => "clearance/sessions#destroy", as: "sign_out"
get "/sign_up" => "clearance/users#new", as: "sign_up"
[...]

Feel free to modify these as needed.

To override controllers’ methods, create a new controller and subclass it from one of the existing ones: Clearance::PasswordsController, Clearance::SessionsController or Clearance::UsersController. Now you can redefine methods as you see fit. Just don’t forget to update the routes to point to your new controller.

Modifying views is simple, as well. Run:

$ rails generate clearance:views

to copy all the default views in your views folder and change them as you see fit.

By default Clearance is going to use your application’s layout, but this can be changed as well. If you need a custom layout to be rendered for one of the controllers, add the following code to the initializer file:

config/initializers/clearance.rb

[...]
Clearance::PasswordsController.layout 'my_passwords_layout'
Clearance::SessionsController.layout 'my_sessions_layout'
Clearance::UsersController.layout 'my_admin_layout'
[...]

When you copy the default views using the command shown above, the I18n file will be copied as well. Use it to change translations, as necessary.

The default User model has a bunch of methods that can be redefined, too.

Additional Sign In Checks

By default, Clearance only checks the user’s e-mail and password when authenticating. Suppose, however, that account is suspended and we don’t want to allow those users to login. This can be implemented with so-called “sign-in guards”.

First of all, add a new field to your users table:

$ rails g migration add_suspended_to_users suspended:boolean

Modify the migration:

xxx_add_suspended_to_users.rb

class AddSuspendedToUsers < ActiveRecord::Migration
  def change
    add_column :users, :suspended, :boolean, default: false, index: true
  end
end

and apply it:

$ rake db:migrate

Now, open the initializer file and change it, like so:

config/initializers/clearance.rb

class SuspendedCheckGuard < Clearance::SignInGuard
  def call
    if suspended?
      failure("Your account is suspended.")
    else
      next_guard
    end
  end

  def suspended?
    current_user.suspended?
  end
end

Clearance.configure do |config|
  config.sign_in_guards = [SuspendedCheckGuard]
  [...]
end

Feel free to extract SuspendedCheckGuard to another file.

The idea behind SignInGuard is pretty simple: it checks a stack of conditions before signing in a user.
Each guard is run in order and hands the session off to the next guard in the stack.

SignInGuard responds to a call method. It is initialized with a session and the current stack. On success,
a guard should call the next guard or return SuccessStatus.new if you don’t want any subsequent guards to run. On failure, a guard should call FailureStatus.new(failure_message).

Clearance provides the SignInGuard base class that can be inherited. This class already implement methods like signed_in? and current_user.

In our example we define the call method that checks whether the user’s account is suspended. If yes – set the failure message and forbid login. This message will be displayed in the flash. If no – run the next check if it is present in the stack. Once all checks are run successfully, the user is logged in.

Now you can boot your server and check how the app is working!

Conclusion

In this article we discussed Clearance – an opinionated authentication and authorization system. I encourage you to browse the project’s wiki as it has entries explaining how to customize Clearance further. I think that this library is a great alternative to Devise if you need to quickly start off and do not need all the features Devise provides.

Have you ever used Clearance? Would you consider using it in future? Share your opinion!

As always, I thank you for staying with me and see you soon!

CSS Master, 3rd Edition