#283 Authentication with Sorcery
Back in episode 250 [watch, read] we added authentication to a Rails application from scratch. If you prefer to use an existing third-party solution there are a number of gems that will do some of the work for you and in this episode we’ll take a look at one called Sorcery.
Sorcery is a simple solution. It offers only about twenty methods but these are enough to provide all the authentication features we’ll need. Despite its simplicity it is full-featured and also modular so that we can choose to enable only the parts we need, such as password resetting, activity logging and so on. Sorcery works at a lower level than other authentication gems leaving it up to us to write the controller and view layers. In this episode we’ll use it to add authentication to an existing Rails application.
The application we’ll be working with is very simple. It has a welcome page that has a link to a “secret” page. The secret page can currently be viewed by anyone but we want to restrict it to only users who have logged in. To do this we’ll need to add authentication to the application and this is where Sorcery comes in.
Sorcery comes as a gem and is installed in the usual way by adding a reference to it in the
Gemfile and then running
Once Bundler has finished we’ll need to run the following command to add Sorcery’s initializer file. (More about this later.)
$ rake sorcery:bootstrap
Next we’ll generate a
sorcery_migration. We use this to choose the Sorcery modules we want to to include. We’ll include the
core module which is necessary for simple, password-based authentication and the
remember_me module. For a full list of the modules check Sorcery’s README.
$ rails g sorcery_migration core remember_me create db/migrate/20110914221626_sorcery_core.rb create db/migrate/20110914221627_sorcery_remember_me.rb
The command creates a number of migration files depending on the modules we’ve chosen. If we look at the
sorcery_core migration we’ll see the attributes that are added to the new
class SorceryCore < ActiveRecord::Migration def self.up create_table :users do |t| t.string :username, :null => false t.string :email, :default => nil t.string :crypted_password, :default => nil t.string :salt, :default => nil t.timestamps end end def self.down drop_table :users end end
By default the migration creates a
username field. We don’t want a
username field as well as an
class SorceryCore < ActiveRecord::Migration def self.up create_table :users do |t| # t.string :username, :null => false t.string :email, :default => nil t.string :crypted_password, :default => nil t.string :salt, :default => nil t.timestamps end end end
We’ll also need to configure Sorcery so that it uses
username. We do this by modifying Sorcery’s initializer file. At the top of this file we need to specify the modules that we want to enable. Apart from the
core module we’re only using
remember_me so this is the only one we need to add here.
# The first thing you need to configure is which modules you need in your app. # The default is nothing which will include only core features (password encryption, login/logout). # Available submodules are: :user_activation, :http_basic_auth, :remember_me, # :reset_password, :session_timeout, :brute_force_protection, :activity_logging, :external Rails.application.config.sorcery.submodules = [:remember_me] # Rest of file omitted.
There are many other configuration options we can enable and set here and they’re all nicely documented in the file. We won’t need to change most of them, but one we do have to alter is
username_attribute_name. We’ll enable this option and change its value to
config.user_config do |user| # -- core -- user.username_attribute_name = :email # change default username # attribute, for example, # to use :email as the login. # Other options omitted. end # This line must come after the 'user config' block. config.user_class = "User" # define which model authenticates # with sorcery. end
At the bottom of the file is a configuration item that specifies the name of the model that Sorcery uses for authentication, by default
User. Our application doesn’t have a
User model yet so we’ll create one now. We already have a migration file to specify the fields for
User so we’ll tell Rails not to generate one when it creates the model.
$ rails g model user --skip-migration
To enable Sorcery in the
User model we just have to add one line of code.
class User < ActiveRecord::Base authenticates_with_sorcery! end
This adds a number of methods to the
User model to handle authentication. It doesn’t, however, add validations or protect any attributes; it’s up to us to add these.
class User < ActiveRecord::Base authenticates_with_sorcery! attr_accessible :email, :password, :password_confirmation validates_confirmation_of :password validates_presence_of :password, :on => :create validates_presence_of :email validates_uniqueness_of :email end
Now is a good time to run the migrations so that the
users table is created.
$ rake db:migrate
Adding Controllers and Views
Now that we have the
User model set up we’ll generate some controllers to go with it. First we’ll create a
UsersController to handle the sign-up process.
$ rails g controller users new
We’ll also need a
SessionsController to handle logging in.
$ rails g controller sessions new
This is similar to what we did in episode 250 when we created authentication from scratch so we’ll go through this fairly quickly. The
UsersController will be pretty standard, building a new
User in the
new action and creating a new
User from the supplied parameters in
class UsersController < ApplicationController def new @user = User.new end def create @user = User.new(params[:user]) if @user.save redirect_to root_url, :notice => "Signed up!" else render :new end end end
new template will be fairly standard too, with a form for creating a new user with
password_confirmation fields along with some code for showing any validation errors.
<h1>Sign Up</h1> <%= form_for @user do |f| %> <% if @user.errors.any? %> <div class="error_messages"> <h2>Form is invalid</h2> <ul> <% for message in @user.errors.full_messages %> <li><%= message %></li> <% end %> </ul> </div> <% end %> <div class="field"> <%= f.label :email %> <%= f.text_field :email %> </div> <div class="field"> <%= f.label :password %> <%= f.password_field :password %> </div> <div class="field"> <%= f.label :password_confirmation %> <%= f.password_field :password_confirmation %> </div> <div class="actions"><%= f.submit %></div> <% end %>
Things get more interesting in the
SessionsController. We have a
new action here, but we don’t need to add any code to it so we’ll move on to the template. Here we’ll need a simple login form with textfields for the
password, and a
checkbox for the
<h1>Log in</h1> <%= form_tag sessions_path do %> <div class="field"> <%= label_tag :email %> <%= text_field_tag :email, params[:email] %> </div> <div class="field"> <%= label_tag :password %> <%= password_field_tag :password %> </div> <div class="field"> <%= check_box_tag :remember_me, 1, params[:remember_me] %> <%= label_tag :remember_me %> </div> <div class="actions"><%= submit_tag "Log in" %></div> <% end %>
We’ll need to write a
create action to process the login form. Sorcery provides a method called
login that can take three parameters: a username or email address, a password that was entered and the value of the
remember_me checkbox. This method will perform the authentication and return a
User if a matching one is found. We can check for this and redirect back to the home page if a user is found. If not we’ll display a flash message and show the login form again.
class SessionsController < ApplicationController def new end def create user = login(params[:email], params[:password], params[:remember_me]) if user redirect_back_or_to root_url, :notice => "Logged in!" else flash.now.alert = "Email or password was invalid." end end end
Instead of using
redirect_to to redirect back to the home page when a user is found we’ll use a method that Sorcery gives us called
redirect_back_or_to. This behaves similarly to
redirect_to but if a URL is stored by Sorcery it will redirect back to that URL rather than to the one specified in the code. This is useful because it means that if we require a user to log in when they visit a certain page Sorcery will take them to the login page and then return them back to the page they tried to visit once they’d successfully logged in.
We still need a way for a user to be able to log out so we’ll also add a
destroy action to the controller. Sorcery provides a method called
logout and that’s all we need to call to log the user out. Once we’ve logged a user out we’ll redirect them back to the home page.
def destroy logout redirect_to root_url, :notice => "Logged out!" end
Next we’ll go into our routes file and wire everything up, replacing the default generated actions with these routes:
Auth::Application.routes.draw do get "logout" => "sessions#destroy", :as => "logout" get "login" => "sessions#new", :as => "login" get "signup" => "users#new", :as => "signup" resources :users resources :sessions get "secret" => "home#secret", :as => "secret" root :to => "home#index" end
We now have various named routes and a couple of resources to handle all of the functionality related to authentication.
Now that we have these new pages we’ll need some links so that users can access them. We’ll add them to the layout page so that they’re visible on every page of the application. We have a
current_user method available to us so we can check to see if we have a logged-in user. If we have we’ll show their email address along with a “Log out” link. If there’s no current user we’ll show links to pages that will let them either sign up or log in.
We’re ready now. to test the site out. If we go to the home page we’ll now see the “Sign up” and “Log in” links.
If we click “Sign up” we’ll see the signup form and we can register on the site. We can then click “Log in” and log in with our registration information.
Now we’re logged into the site we can visit the secret page. If we log out and try to visit the secret page, though, we can still see it. We need to add some authorization to restrict the page to logged-in users.
The secret page is an action in the
HomeController. We can use a
before_filter that Sorcery provides called
require_login to limit access to actions. We’ll use it to restrict access to the secret action.
class HomeController < ApplicationController before_filter :require_login, :only => :secret def index end def secret end end
When this filter is triggered Sorcery calls its
not_authenticated method. It’s a good idea to override this method in the
ApplicationController so that we can control what happens when the authorization fails. We’ll redirect to the login page and show an alert message.
class ApplicationController < ActionController::Base protect_from_forgery private def not_authenticated redirect_to login_url, :alert => "First log in to view this page." end end
If we try to visit the secret page now while logged-out we’ll be redirected to the login page and the alert message will be shown.
When we log in we’ll be redirected back to the secret page as Sorcery will remember the page we were trying to visit when we were redirected to the login page.
That’s it for our episode on Sorcery. It comes with many more features that we’ve covered here and which are covered in the documentation. If you’re looking for an authentication solution that works at a slightly lower level then Sorcery is well worth taking a look at.