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 lineinclude 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 theApplicationController
. 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!
Frequently Asked Questions (FAQs) about Rails Authentication with Clearance
How do I install the Clearance gem in my Rails application?
To install the Clearance gem in your Rails application, you need to add the gem to your Gemfile. Open your Gemfile and add the following line: gem 'clearance'
. Then, run bundle install
in your terminal to install the gem. After the gem is installed, you need to run rails generate clearance:install
to generate the necessary files and migrations. Finally, run rails db:migrate
to apply the migrations to your database.
How do I customize the Clearance views?
Clearance provides a set of default views for authentication. However, you can customize these views to fit your application’s needs. To do this, run rails generate clearance:views
. This will copy the default views to your application, allowing you to modify them as needed.
How do I configure Clearance to use a custom user model?
By default, Clearance uses a User model for authentication. If you want to use a different model, you can configure Clearance to do so. In your config/initializers/clearance.rb
file, set the Clearance.configure
block to use your custom model. For example, if you have a Member model, you would set config.user_model = Member
.
How do I add additional fields to the Clearance user model?
If you need to add additional fields to the User model, you can do so by creating a new migration. For example, if you want to add a first_name
and last_name
field, you would run rails generate migration AddNameToUsers first_name:string last_name:string
. Then, run rails db:migrate
to apply the migration.
How do I restrict access to certain pages with Clearance?
Clearance provides a require_login
method that you can use to restrict access to certain pages. In your controller, you can add a before_action
callback to call this method. For example, before_action :require_login, only: [:edit, :update]
would restrict access to the edit and update actions to logged-in users.
How do I test Clearance in my Rails application?
Clearance provides a set of helpers that you can use in your tests. To use these helpers, include Clearance::Test::ControllerHelpers
in your test file. Then, you can use methods like sign_in
and sign_out
to simulate user authentication in your tests.
How do I change the default Clearance routes?
Clearance generates a set of default routes for authentication. If you want to change these routes, you can do so in your config/routes.rb
file. For example, you could change the sign in route to /login
by adding get '/login' => 'clearance/sessions#new'
to your routes file.
How do I use Clearance with Rails API?
If you’re building a Rails API, you can still use Clearance for authentication. However, you’ll need to configure Clearance to use API mode. In your config/initializers/clearance.rb
file, set config.api = true
.
How do I upgrade Clearance in my Rails application?
To upgrade Clearance, you need to update the gem in your Gemfile. Change the version number to the latest version and run bundle update clearance
. Then, check the Clearance GitHub page for any necessary migration or configuration changes.
How do I uninstall Clearance from my Rails application?
To uninstall Clearance, remove the gem from your Gemfile and run bundle install
. Then, remove any Clearance-related code from your application, including migrations, models, controllers, and views. Finally, remove the Clearance initializer from your config/initializers
directory.
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.