Magical Authentication with Sorcery

Share this article

Magician Hat

Authentication is a vital part of many web apps. I should not need to persuade you that an authentication must be easy to use and well-protected at the same time. You may either write it from scratch or use one of the many gems available out there.

This article is the first in the upcoming series devoted to authentication in Rails. We are going to take a look at Sorcery, a less well-known, but very convenient and easy to use gem created by Noam Ben-Ari and other folks. In comparison to Devise, Sorcery is a bit more low level and requires the developer to perform some additional actions. However, this is a good thing because you can cherry-pick only the required functionality.

Additional options are added with the help of submodules. Initially, Sorcery provides only the most minimal set of features. There are submodules to enable user activation, brute force protection, OAuth 2 support and more. It is really up to you to decide on what your app needs. “Less is more” is one of the main principles of Sorcery.

I hope you are excited to get to know Sorcery better. :) Read on and let’s build a demo app together!

The source code is available on GitHub.

The working demo is available at sitepoint-sorcery.herokuapp.com.

Preparing the App

I am going to call my demo app “Magical” – we’re integrating magic authentication after all:

$ rails new Magical -T

Rails 4.2.0 will be used, but the same solution can be implemented with Rails 3. This demo app will provide no functionality apart from authentication and related features, however we need at least one page for testing purposes that should be accessible only by authenticated users, so create a basic PagesController:

pages_controller.rb

class PagesController < ApplicationController
  def index
  end
end

the corresponding view:

views/pages/index.html.erb

<h1>Welcome!</h1>

<p>Restricted area for authorized users only.</p>

and add the routes:

config/routes.rb

[...]
get '/secret', to: 'pages#index', as: :secret
root to: 'pages#index'
[...]

I am also going to use Twitter Bootstrap to style the app a bit:

Gemfile

[...]
gem 'bootstrap-sass', '~> 3.3.3'
[...]

application.scss

@import "bootstrap-sprockets";
@import "bootstrap";
@import "bootstrap/theme";

.nav > li > span {
  display: block;
  padding-top: 15px;
  padding-bottom: 15px;
  color: #9d9d9d;
}

views/layouts/application.html.erb

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

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

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

Okay, preparations are done…it was fast, wasn’t it? Now, let’s integrate Sorcery into the app!

Integrating Sorcery

Model and migrations

First of all, drop the gem into your Gemfile:

Gemfile

[...]
gem 'sorcery'
[...]

and run

$ bundle install

Sorcery’s integration should not raise any difficulties, however there are a couple of known incompatibilities that you should take into consideration.

We need to generate Sorcery’s config and migration. Initially Sorcery provides only the most basic functionality – authentication itself. No brute force protection, no “remember me”, no “restore password”, not even email validity checking. This, however, means that you can cherry-pick only the features that you really need.

Go ahead and run the command:

$ rails g sorcery:install

This will create the config/initializers/sorcery.rb file, User model, and migration. The User is given three fields by Sorcery:

  • crypted_password (string)
  • salt (string)
  • email (string)

If you wish to specify a different name for the model, provide the --model flag:

$ rails g sorcery:install --model Admin

Open up the newly generated migration file and add the following line inside the create_table method:

xxx_sorcery_core.rb

[...]
t.string :name
[...]

This way we are ensuring that users can also provide their names. The migration can now be applied:

$ rake db:migrate

On to the model. If you open the models/user.rb file, you’ll notice that the

models/user.rb

[...]
authenticates_with_sorcery!
[...]

line is present there. It adds some Sorcery’s methods to the model. What we have to do here is add some validations, because initially none are present. This means that any email and password (including blank) may be provided.

models/user.rb

[...]
validates :password, length: { minimum: 3 }
validates :password, confirmation: true
validates :email, uniqueness: true
[...]

I am also requiring a minimum length to the password, requiring a confirmation, and making sure email is unique.

How about email format? You might write your own regular expression, but it appears that this regexp is crazy long. Instead, grab an existing gem to solve this problem:

Gemfile

[...]
gem 'validates_email_format_of'
[...]

Don’t forget to run

$ bundle install

Then modify the model like this:

models/user.rb

[...]
validates :email, uniqueness: true, email_format: { message: 'has invalid format' }
[...]

Now we can be sure that email have the correct format. It is high time to proceed to controllers.

Sign Up

Two controllers will be needed: one to handle user registrations (and possibly profile updating or deletion) and another one to handle logging in and out.

Let’s start with registrations. Call this controller UsersController:

users_controller.rb

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

  def create
    @user = User.new(user_params)
    if @user.save
      flash[:success] = 'Welcome!'
      redirect_to root_path
    else
      render 'new'
    end
  end

  private

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

The view:

views/users/new.html.erb

<h1>Registration</h1>

<%= form_for @user do |f| %>
  <%= render 'shared/errors', object: @user %>
  <div class="form-group">
    <%= f.label :name %>
    <%= f.text_field :name, class: 'form-control', required: true %>
  </div>

  <div class="form-group">
    <%= f.label :email %>
    <%= f.email_field :email, class: 'form-control', required: true %>
  </div>
  <div class="form-group">
    <%= f.label :password %>
    <%= f.password_field :password, class: 'form-control', required: true %>
  </div>
  <div class="form-group">
    <%= f.label :password_confirmation %>
    <%= f.password_field :password_confirmation, class: 'form-control', required: true %>
  </div>
  <%= f.submit 'Register', class: 'btn btn-primary btn-lg' %>
<% end %>

Sorcery uses the bcrypt-ruby gem to secure passwords. This means that password are never stored in plain text – only their digest is present in the database ( in the crypted_password field). Therefore, password and password_confirmation are virtual attributes without corresponding table fields.

You may also have noted the salt field – it is a random string used to further protect the password. You see, digests generated by hash functions are nearly impossible to invert and restore to the original message. What can be done, however, is generating a dictionary of words and the corresponding digests. As long as the same message always has the same digest, an attacker who gained access to the database can search digest through the dictionary and look up the corresponding string. The salt is meant to prevent such attacks. It is generated when a user is first created and used in hashing process like this:

hash(salt + password)

It is impossible to build a dictionary for each unique salt, so the password becomes truly protected. You may read more here.

By the way, you may use other encryption algorithms by overriding the user.encryption_algorithm = option inside the config/initializers/sorcery.rb file.

Let’s add the routes:

config/routes.rb

[...]
resources :users, only: [:new, :create]
get '/sign_up', to: 'users#new', as: :sign_up
[...]

Okay, now users may sign up, but they will not be logged in by default. To do that the login method can be used that accepts at least two arguments: email and password. Add it to the controller:

users_controller.rb

[...]
def create
  @user = User.new(user_params)
  if @user.save
    login(params[:user][:email], params[:user][:password])
[...]

Try to sign up…everything should be working fine.

Log In and Out

The next step is implementing functionality for logging in and out. Create a new SessionsController:

sessions_controller.rb

class SessionsController < ApplicationController
  def new
  end

  def create
    if login(params[:email], params[:password])
      flash[:success] = 'Welcome back!'
      redirect_to root_path
    else
      flash.now[:warning] = 'E-mail and/or password is incorrect.'
      render 'new'
    end
  end

  def destroy
    logout
    flash[:success] = 'See you!'
    redirect_to log_in_path
  end
end

The same login method is used in the create action. If the user has provided an incorrect email or password, this method returns nil, so an error message would be shown. The logout method inside the destroy action does pretty much what it says – logs the user out.

Now the view:

views/sessions/new.html.erb

<h1>Log In</h1>
<%= form_tag sessions_path, method: :post do %>
  <div class="form-group">
    <%= label_tag :email %>
    <%= email_field_tag :email, nil, class: 'form-control', required: true %>
  </div>

  <div class="form-group">
    <%= label_tag :password %>
    <%= password_field_tag :password, nil, class: 'form-control', required: true %>
  </div>

  <%= submit_tag 'Log In', class: 'btn btn-primary btn-lg' %>
<% end %>

And the routes:

config/routes.rb

[...]
resources :sessions, only: [:new, :create, :destroy]
get '/log_in', to: 'sessions#new', as: :log_in
delete '/log_out', to: 'sessions#destroy', as: :log_out
[...]

With this in place, it’s time to tweak the layout to display all the necessary links.

Displaying the Links

We want to present “Sign Up” and “Log In” links to users who are not currently authenticated. “Log Out” and “Secret Page” links should be shown once they sign in. To check whether the user is authenticated or not, use the current_user method, which returns a user record or nil:

views/layouts/application.html.erb

[...]
<div id="navbar">
  <ul class="nav navbar-nav">
    <% if current_user %>
      <li><%= link_to 'Secret Page', secret_path %></li>
    <% else %>
      <li><%= link_to 'Sign Up', sign_up_path %></li>
      <li><%= link_to 'Log In', log_in_path %></li>
    <% end %>
  </ul>
  <% if current_user %>
    <ul class="nav navbar-nav pull-right">
      <li><span><%= current_user.name %></span></li>
      <li><%= link_to 'Log Out', log_out_path, method: :delete %></li>
    </ul>
  <% end %>
</div>
[...]

This is pretty straightforward, isn’t it? However, the secret page still can be accessed just by typing its URL directly. What we need to do is add some kind of check in the page controller.

Restricting Access

Luckily, Sorcery provides a require_login method that restricts certain pages from unauthorized users. It should be used as a before_action like this:

application_controller.rb

[...]
before_action :require_login
[...]

However this would result in restricting access to all pages. Obviously, users who are not logged in should be able to visit Log In and Sign Up pages. Thus, use skip_before_action:

users_controller.rb

[...]
skip_before_action :require_login, only: [:new, :create]
[...]

sessions_controller.rb

[...]
skip_before_action :require_login, except: [:destroy]
[...]

How about the action that happens when an unauthenticated user tries to open a restricted page? Open Sorcery’s initializer file:

config/initializers/sorcery.rb

[...]
# What controller action to call for non-authenticated users. You can also
# override the 'not_authenticated' method of course.
# Default: `:not_authenticated`
#
# config.not_authenticated_action =
[...]

The not_authenticated method is what we need. Create it:

application_controller.rb

[...]
private

def not_authenticated
  flash[:warning] = 'You have to authenticate to access this page.'
  redirect_to log_in_path
end
[...]

There is one more small improvement that may be added. Currently, when an unauthenticated user opens a restricted page, they will be redirected to the log in page. After logging in, they’re taken to the main page of the site. This is not very convenient. It would be better to redirect a user to the page they were trying to see. Meet the redirect_back_or_to method. This method will either redirect the user back to where they came from or to the specified page:

sessions_controller.rb

[...]
def create
  if login(params[:email], params[:password])
    flash[:success] = 'Welcome back!'
    redirect_back_or_to root_path
[...]

Do You Still Remember Me?

You’re probably used to the little checkbox labeled “remember me” on sign in pages. How about adding it to out app as well?

We can take advantage of Sorcery’s submodules for this task. Each submodule provides its own piece of functionality and can be hooked up independently. Think of them as of Lego blocks.

The submodule that we are currently interested in is called RememberMe. Run the following command to copy the required migration:

$ rails g sorcery:install remember_me  --only-submodules

In some documentation, you might find the --migrations flag instead of --only-submodules but the latter one is preferred, as --migrations is deprecated. This migration will add two columns to the users table:

  • remember_me_token (string)
  • remember_me_token_expires_at (datetime)

This token is used to “remember” the user and, obviously, it should not be valid forever. Apply the migration:

$ rake db:migrate

Now, register the new submodule:

config/initializers/sorcery.rb

[...]
Rails.application.config.sorcery.submodules = [:remember_me]
[...]

If you wish to tweak the duration of the token, look for the user.remember_me_for option in the sorcery.rb file. The default value is one week.

Add the checkbox to the log in form:

views/sessions/new.html.erb

[...]
<%= form_tag sessions_path, method: :post do %>
  [...]
  <div class="form-group">
    <%= label_tag :remember_me %>
    <%= check_box_tag :remember_me %>
  </div>
  [...]
<% end %>

Lastly, tweak the corresponding controller’s method:

sessions_controller.rb

[...]
def create
  if login(params[:email], params[:password], params[:remember_me])
    flash[:success] = 'Welcome back!'
    redirect_back_or_to root_path
  else
    flash.now[:warning] = 'E-mail and/or password is incorrect.'
    render 'new'
  end
end
[...]

The login method can also accept the third optional parameter specifying if the user should be remembered or not.

Activate Yourself!

On many websites, the user has to activate their account by visiting a link sent to them via email before actually logging in. Let’s implement the same feature. Install a new submodule:

$ rails g sorcery:install user_activation  --only-submodules

and apply the migration:

$ rake db:migrate

That is going to add the following fields:

  • activation_state (string)
  • activation_token (string)
  • activation_token_expires_at (datetime)

Register the submodule:

config/initializers/sorcery.rb

[...]
Rails.application.config.sorcery.submodules = [:user_activation]
[...]

Also, tweak the settings:

config/initializers/sorcery.rb

[...]
user.user_activation_mailer = UserMailer
[...]

There are also a couple of settings worth mentioning:

  • user.activation_mailer_disabled – if set to true, an email with an activation link will not be sent automatically, allowing you to decide when to send it. Default value is false.
  • prevent_non_active_users_to_login – whether non-activated users should be able to log in. Default is false.

Generate the mailer to handle sending the email:

$ rails g mailer UserMailer activation_needed_email activation_success_email

Delete all the .text.erb files from the layouts and user_mailer directory. Now modify the mailer like this:

mailers/user_mailer.rb

class UserMailer < ApplicationMailer
  def activation_needed_email(user)
    @user = user
    mail(to: user.email, subject: "Account activation")
  end

  def activation_success_email(user)
    @user = user
    mail(to: user.email, subject: "Your account is now activated")
  end
end

As you can see, Sorcery requires two methods:

  • activation_needed_email sends an email with the activation link
  • activation_success_email sends a confirmation email saying the account was activated successfully.

Actually, you can disable sending the “successful” email by setting activation_success_email_method_name in sorcery.rb to nil.

The views:

views/user_mailer/activation_needed_email.html.erb

<p>Welcome, <%= @user.name %>!</p>

<p>To login to the site, just follow <%= link_to 'this link', activate_user_url(@user.activation_token) %>.</p>

<p>Thanks for joining and have a great day!</p>

views/user_mailer/activation_success_email.html.erb

<p>Congratulations, <%= @user.name %>!</p>

<p>You have successfully activated your account.</p>

<p>You may now proceed to <%= link_to 'log in page', log_in_url %>.</p>

<p>Thanks for joining and have a great day!</p>

For link helpers to work we will need to do some configuration:

config/environments/development.rb

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

Please note that emails won’t be actually sent in development – you will only be able to see their contents and meta information in the console. Set config.action_mailer.perform_deliveries to true in development.rb to change this behavior.

For production, you will have to configure the SMTP settings. Some examples can be found here. In my demo app, I am going to disable user activation.

To complete this step, we have to add a controller method and a route:

config/routes.rb

[...]
resources :users, only: [:new, :create] do
  member do
    get :activate
  end
end
[...]

users_controller.rb

[...]
skip_before_action :require_login

def activate
  if @user = User.load_from_activation_token(params[:id])
    @user.activate!
    flash[:success] = 'User was successfully activated.'
    redirect_to log_in_path
  else
    flash[:warning] = 'Cannot activate this user.'
    redirect_to root_path
  end
end
[...]

load_from_activation_token is a method presented by Sorcery that finds a resource by an activation token. activate! actually activates the account and saves the result to the database.

Awesome! You may now go check how this all is working.

Integrating with DelayedJob

You probably noticed that it takes some time for an email to be sent and the page won’t load until this operation is completed. This is not a good user experience, as sending the email might take a long time. To fix this, the email sending process should be made asynchronous.

DelayedJob by CollectiveIdea can be utilized to achieve this goal.

Drop in a new gem:

Gemfile

[...]
gem 'delayed_job_active_record'
# or
gem 'delayed_job_mongoid'
[...]

and run

$ bundle install

Generate and apply DelayedJob’s migration issue:

$ rails generate delayed_job:active_record
$ rake db:migrate

Also, add the following line to the application config to set the queueing backend:

config/application.rb

[...]
config.active_job.queue_adapter = :delayed_job
[...]

This should be done only for Rails 4.2+.

Lastly, place the following code at the end of Sorcery’s initializer file:

config/initializers/sorcery.rb

[...]
module Sorcery
  module Model
    module InstanceMethods
      def generic_send_email(method, mailer)
        config = sorcery_config
        mail = config.send(mailer).delay.send(config.send(method), self)
      end
    end
  end
end

The email sending is now performed asynchronously, yay!

To test this on the local machine, run

$ rake jobs:work

so that DelayedJob starts processing jobs and boot the server by issuing

$ rails s

in a separate console tab. Next register a new user and navigate to DelayedJob’s console tab. You will see something like

[Worker(host:xxx pid:7128)] Job UserMailer.send (id=1) RUNNING
[Worker(host:xxx pid:7128)] Job UserMailer.send (id=1) COMPLETED after 2.2951
[Worker(host:xxx pid:7128)] 1 jobs processed at 0.4057 j/s, 0 failed

This means that the integration was completed successfully.

Protecting from Brute Force

Brute force is probably the most widely known type of attack. Basically, the attacker tries to “guess” a password by trying one combination of symbols after another. To be secure, an account should be locked out after some unsuccessful log in attempts.

Add another Sorcery submodule:

$ rails g sorcery:install brute_force_protection  --only-submodules

This generates a migration that is going to add these columns:

  • failed_logins_count (integer) – how many times in a row has the user made unsuccessful attempts to log in
  • lock_expires_at (default) – when the account will be unlocked (if it is locked)
  • unlock_token (string) – a random token to unlock an account

Apply the migration:

$ rake db:migrate

Register the new module:

config/initializers/sorcery.rb

[...]
Rails.application.config.sorcery.submodules = [:brute_force_protection]
[...]

That’s it. Sorcery will take care of the rest, but you may want to tweak some settings, such as:

  • user.consecutive_login_retries_amount_limit – how many unsuccessful attempts are allowed. The default is 50.
  • user.login_lock_time_period – how long the user should be locked out. The default is 3600 seconds. Provide 0 to lock out the user indefinitely.
  • user.unlock_token_mailer and user.unlock_token_email_method_name – class and method to send emails to users with unlock tokens (by default they are not set up).

If you want to let the user unlock their account, apart from creating a mailer, you will need a separate controller (ResetPasswordsController, for example) with a method similar to this one:

[...]
def create
  u = User.load_from_unlock_token(params[:token])
  u.unlock!
  flash[:success] = 'Your account was unlocked!'
  redirect_to root_url
end
[...]

load_from_unlock_token is a method provided by Sorcery that searches for a user by the provided unlock token. unlock!, in turn, removes the lock. ! at the end of the method’s name says that the user will immediately be saved, so you don’t have to call u.save.

Log the Activity

Activity logging is a submodule that helps implement functionality like “who is currently online”. You know what to do:

$ rails g sorcery:install activity_logging  --only-submodules

that will add the following fields:

  • last_login_at (datetime)
  • last_logout_at (datetime)
  • last_activity_at (datetime)
  • last_login_from_ip_address (string)

Apply the migration:

$ rake db:migrate

Register the submodule:

config/initializers/sorcery.rb

[...]
Rails.application.config.sorcery.submodules = [:activity_logging]
[...]

We need a method that returns currently active users. Sorcery used to provide the current_users method, but it was decided to remove it. Therefore, let’s write our own version:

application_controller.rb

[...]
private

def current_users
  User.current_users
end

helper_method :current_users
[...]

user.rb

[...]
class << self
  def current_users
    where("#{sorcery_config.last_activity_at_attribute_name} IS NOT NULL") \
.where("#{sorcery_config.last_logout_at_attribute_name} IS NULL
  OR #{sorcery_config.last_activity_at_attribute_name} > #{sorcery_config.last_logout_at_attribute_name}") \
.where("#{sorcery_config.last_activity_at_attribute_name} > ? ", sorcery_config.activity_timeout.seconds.ago.utc.to_s(:db))
  end
end
[...]

There are some other examples on the Sorcery wiki.

Now, just use the current_users method to display user’s names:

views/layouts/application.html.erb

[...]
<div class="col-sm-9">
  <%= yield %>
</div>
<%= render 'shared/current_users' %>
[...]

views/shared/_current_users.html.erb

<div class="col-sm-3 well well-sm">
  <h3>Currently active users:</h3>
  <ul>
    <% current_users.each do |user| %>
      <li><%= user.name %></li>
    <% end %>
  </ul>
</div>

Great!

If you are interested, this module simply sets some callbacks that fire after login, before logout, and after every other request. These callbacks update the corresponding fields. For example, here is the code that runs after each user request.

Update 2015/04/01: Forgot Password

This update was, once again, inspired by one of the readers who asked me to show how to implement password resetting functionality with Sorcery. Thanks to all of you for such great feedback!

Users tend to forget their passwords, so presenting an option to reset them is absolutely necessary for any authentication system. With Sorcery, we can do this really fast.

Install the new ResetPassword module as always:

$ rails g sorcery:install reset_password --only-submodules

This will generate a migration adding the following fields to the users table:

  • reset_password_token (string, index)
  • reset_password_token_expires_at, (datetime)
  • reset_password_email_sent_at, (datetime)

reset_password_token is a random string that will be generated when password reset instructions are requested. This token is only valid for a limited period of time and is nullified as soon as the user changes his password. It will be later used to fetch a user record and change the password for it, so tokens should be kept safe.

Make sure that the module was registered:

config/initializers/sorcery.rb

[...]
    Rails.application.config.sorcery.submodules = [:reset_password]
    [...]

Now provide the “Forgot your password?” link:

views/sessions/new.html.erb

<p><%= link_to 'Forgot your password?', new_reset_password_>path %></p>

Set up some routes:

config/routes.rb

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

and create a new controller:

reset_passwords_controller.rb

class ResetPasswordsController < ApplicationController
      skip_before_filter :require_login

      def new
      end
    end

Create the first view where the user will enter the e-mail to receive password reset instructions:

views/reset_passwords/new.html.erb

<h1>Reset Password</h1>

    <%= form_tag reset_passwords_path, method: :post do %>
      <div class="form-group">
        <%= label_tag :email %>
        <%= text_field_tag :email, nil, class: 'form-control', required: true %>
      </div>

      <%= submit_tag "Send reset instructions", class: 'btn btn-primary btn-lg' %>
    <% end %>

Now the create method:

reset_passwords_controller.rb

[...]
  def create
    @user = User.find_by_email(params[:email])
    @user.deliver_reset_password_instructions! if @user
    flash[:success] = 'Instructions have been sent to your email.'
    redirect_to log_in_path
  end
    [...]

deliver_reset_password_instructions! is the method provided by Sorcery that will actually rely on your own mailer to send password reset instructions. We already have the UserMailer in place, so let’s just add a new method there:

mailers/user_mailer.rb

[...]
  def reset_password_email(user)
    @user = user
    @url  = edit_reset_password_url(@user.reset_password_token)
    mail(to: user.email,
         subject: "Your password has been reset")
  end
    [...]

Add the corresponding view:

views/user_mailer/reset_password_email.html.erb

<p>Hello, <%= @user.name %>!</p>

    <p>Someone (hopefully, you) have requested to reset your password.</p>

    <p>To choose a new password, follow this link: <%= link_to @url, @url %>.</p>

We also have to provide the mailer’s name in Sorcery’s config file:

config/initializers/sorcery.rb

[...]
    user.reset_password_mailer = UserMailer
    [...]

You may override the user.reset_password_email_method_name if you don’t like the default reset_password_email method name.

Now add the edit action that will be called when a user clicks on the link provided in the e-mail:

reset_passwords_controller.rb

[...]
  def edit
    @token = params[:id]
    @user = User.load_from_reset_password_token(@token)

    not_authenticated if @user.blank?
  end
    [...]

and the view:

views/reset_passwords/edit.html.erb

<h1>Choose a new password</h1>

    <%= form_for @user, url: reset_password_path(@token), method: :patch do |f| %>
      <%= render 'shared/errors', object: @user %>

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

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

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

Lastly, add the update action to handle password changing:

reset_passwords_controller.rb

[...]
  def update
    @token = params[:id]
    @user = User.load_from_reset_password_token(@token)

    not_authenticated && return if @user.blank?

    @user.password_confirmation = params[:user][:password_confirmation]
    if @user.change_password!(params[:user][:password])
      flash[:success] = 'Password was successfully updated.'
      redirect_to log_in_path
    else
      render "edit"
    end
  end
    [...]

And you’re done! Feel free modify this code further and post your questions if you’re in trouble.

Conclusion

We’ve taken a look at Sorcery’s basic setup and its submodules. There are some more of them available, so take a look at the project wiki to learn more.

Have you ever tried using Sorcery in your projects? Did you find it convenient? Share your experiences and don’t hesitate to post your questions in the comments.

Frequently Asked Questions (FAQs) about Magical Authentication with Sorcery

How does Sorcery compare to other authentication systems like Devise?

Sorcery is a lightweight, flexible authentication system for Rails applications. Unlike Devise, which is a full-featured solution, Sorcery provides only the core features for authentication, allowing developers to add additional functionality as needed. This makes it a great choice for developers who want more control over their authentication system. Sorcery is also easier to customize and extend, making it a good fit for complex applications.

Can I use Sorcery for passwordless authentication?

Yes, you can use Sorcery for passwordless authentication. While the default setup of Sorcery involves password-based authentication, it can be customized to support passwordless authentication. This involves sending a unique, temporary link to the user’s email address, which they can use to log in. This can be a more user-friendly approach to authentication, especially for mobile users.

How do I set up Sorcery in my Rails application?

Setting up Sorcery in a Rails application involves adding the Sorcery gem to your Gemfile, running the bundle install command to install it, and then running the Sorcery generator to create the necessary files. You’ll also need to configure Sorcery in your application’s initializer file, and add the necessary routes, controllers, and views for authentication.

How do I customize the authentication process with Sorcery?

Sorcery provides a number of configuration options that allow you to customize the authentication process. These include options for password encryption, session management, and user activation. You can also add additional modules to Sorcery to support features like remember me, reset password, and user activation.

How do I test authentication with Sorcery?

Testing authentication with Sorcery can be done using Rails’ built-in testing tools, along with libraries like RSpec and Capybara. Sorcery provides helper methods that make it easy to log in and out users in your tests, and to test the various aspects of the authentication process.

Can I use Sorcery with other Ruby frameworks?

While Sorcery was designed for Rails, it can also be used with other Ruby frameworks that support Rack middleware, such as Sinatra. This makes it a versatile choice for Ruby developers, regardless of their preferred framework.

How do I handle password resets with Sorcery?

Sorcery provides a reset password module that makes it easy to handle password resets. This involves generating a unique, temporary token for the user, which they can use to reset their password. The module also provides methods for sending the reset password email, and for updating the user’s password.

How secure is Sorcery?

Sorcery is designed to be secure, with features like password encryption, session timeout, and brute force protection. However, like any authentication system, its security depends on how it’s used. It’s important to follow best practices for authentication, such as using secure passwords, protecting against CSRF attacks, and keeping your application’s code and dependencies up to date.

Can I use Sorcery for multi-factor authentication?

While Sorcery doesn’t provide built-in support for multi-factor authentication, it can be extended to support it. This involves adding an additional step to the authentication process, where the user is required to provide a second form of identification, such as a code sent to their mobile device.

How do I handle user activation with Sorcery?

Sorcery provides a user activation module that makes it easy to handle user activation. This involves sending a unique, temporary link to the user’s email address, which they can use to activate their account. The module also provides methods for sending the activation email, and for activating the user’s account.

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
Loading form