Authentication with Devise

Roy van de Water

Covered in this presentation:

Helpful Links

Devise: http://github.com/plataformate/devise

Slides: http://presentations.royvandewater.com/?file=devise.html

Project on Github: http://github.com/royvandewater/devise-samples

Basic Setup

  1. Add devise to Gemfile
  2. Run Devise installation generator
                      rails g devise:install
                    
  3. Add your action mailer default host configuration to the appropriate environments
                        config.action_mailer.default_url_options = { :host => 'localhost:3000' }
                      
  4. Define your homepage in the routes file
                      root :to => "static#index"
                    
  5. Add the alert and notice flash messages to your application layout
                      

    <%= notice %>

    <%= alert %>

  6. Run Devise generator:
                      rails g devise user
                    

Handy Helpers

              <% unless user_signed_in? -%>
                
  • <%= link_to "Sign in", new_user_session_path %>
  • <% else -%>
  • <%= link_to "Sign out", destroy_user_session_path %>
  • <% end -%>
                  before_filter :authenticate_user!
                

    Email Verification

    To require email confirmation, uncomment the following lines from the devise migration:

                  # Rails.root/db/migrations/2011011620391_devise_create_users.rb
                  def self.up
                    create_table(:users) do |t|
                      t.confirmable
                    end
    
                    add_index :users, :confirmation_token,   :unique => true
                  end
                

    And add ":confirmable" to the devise line in your user model:

                  # Rails.root/app/models/user.rb
                  devise :database_authenticatable, :registerable,
                         :recoverable, :rememberable, :trackable, :validatable,
                         :confirmable
                

    Then rerun your migrations and you're done


    Note: The development server must be restarted to recognize "user_confirmation_url" as a valid path

    Custom Views

    By default, devise uses its own views, located inside the gem.


    To use custom sign in and new user registration forms run the following devise generator:

                  rails g devise:views
                

    This pulls in all devise views, allowing them to be changed.


    Note: You can overwrite only the views you want to update. Missing views will automatically be retrieved from the gem

    Custom Actions

    To execute custom code on login/logout, override the sessions controller:

                    # Rails.root/app/controllers/sessions.rb
                    class SessionsController < Devise::SessionsController
    
                      def create
                        logger.info "Attempt to sign in by #{ params[:user][:email] }"
                        super
                      end
    
                      def destroy
                        logger.info "#{ current_user.email } signed out"
                        super
                      end
                    end
                  

    And point the devise routes line at your new controller:

                  # Rails.root/config/routes.rb
                  devise_for :users, :controllers => { :sessions => :sessions }
                

    The views for the overwritten controller must be copied locally into apps/views/sessions

    Custom Encryptor

    Create your own custom encryptor in the lib directory of the rails app:

                # Rails.root/lib/devise/encryptors/rot13.rb
                module Devise
                  module Encryptors
                    class Rot13 < Base
                      def self.digest(password, stretches, salt, pepper)
                        "#{password}-#{salt}".tr! "A-Za-z", "N-ZA-Mn-za-m"
                      end
                    end
                  end
                end
              

    Then require and select your new encryptor in the Devise initializer

                # Rails.root/config/initializers/devise.rb
                require "devise/encryptors/rot13"
    
                Devise.setup do |config|
                  config.encryptor = :rot13
                end
              

    Token Authenticatable

    To enable token authentication uncomment the following lines from the devise migration:

                  # Rails.root/db/migrations/2011011620391_devise_create_users.rb
                  def self.up
                    create_table(:users) do |t|
                      t.token_authenticatable
                    end
                  end
                

    And add ":token_authenticatable" to the devise line in your user model:

                  # Rails.root/app/models/user.rb
                  devise :database_authenticatable, :registerable,
                         :recoverable, :rememberable, :trackable, :validatable,
                         :token_authenticatable
                

    The authentication token can be generated using

                  @user.reset_authentication_token!
                

    The user can now authenticate himself like this:
    http://localhost:3000/?auth_token=OyZSe8ozy_os5acibqNC
    or by passing the auth_token as the username in HTTP Basic Auth

    Single Access Token

    Authentication tokens are permanent by default


    To create a single access token, clear or reset it on token login


    Devise adds the instance method after_token_authentication to the user model and calls it after every successful token login


    To clear the authentication token, add the following to the user model:

                  def after_token_authentication
                    update_attributes :authentication_token => nil
                  end
                

    To reset the authentication, use the built in token reset method:

                  def after_token_authentication
                    reset_authentication_token!
                  end
                

    Using HTTP Basic Auth

    Devise disables basic auth by default


    To enable, uncomment the following line from config/initializers/devise.rb and set its value to true (and restart the server):

                  config.http_authenticatable = true
                

    To test if devise is now accepting HTTP Basic Auth, try the following curl statement:

                  curl -u user@example.com:password http://localhost:3000/users
                

    Simple Administrative Access

    Add a boolean to the user model

                class AddIsAdminToUsers < ActiveRecord::Migration
                  def self.up
                    add_column :users, :is_admin, :boolean
                  end
              

    And add a before filter to whatever actions require admin access

                # In controller
                before_filter :require_admin!, :except => [:index, :show]
    
                # In application controller
                protected
                def require_admin!
                  unless current_user.try :is_admin?
                    redirect_to root_path, :alert => "Access Denied"
                  end
                end
                 
              

    Note: For more complex permissions, including permit and restriction functionality, take a look at cream

    Dual User Roles

    Run the devise generator for your second user type

                rails g devise employee
              

    The employee user now has his own login path, found at new_employee_session_path


    Additionaly helpers include employee_signed_id?, current_employee and the authenticate_employee! before filter


    Actions that need to allow either user type can use the following before filter:

                # In the controller
                before_filter :require_user_or_employee!
    
                # In the application controller
                protected
                def require_user_or_employee!
                  unless user_signed_in? or employee_signed_in?
                    redirect_to root_path, :alert => "Access Denied"
                  end
                end
              

    Login with email or username

    Add the username attribute to the user model:

                rails g add_username_to_users username:string  
              

    Override the find_for_database_authentication method in the user controller:

                protected
                def self.find_for_database_authentication(conditions)
                  login = conditions.delete(:login)
                  where(conditions)
                  .where(["username = :login OR email = :login", 
                         {:login => login}])
                  .first
                end
              

    Uncomment the config.authentication_keys line in the devise initializers and replace :email with :login

                config.authentication_keys = [ :login ]
              

    Add the login as an attr_accessor on the user model

                attr_accessor :login 
              

    Login with email or username (Part 2)

    Update the registration and login view (remember to run the devise:views generator)

                  # app/views/devise/sessions/new.html.erb
                  

    <%= f.label :login %>
    <%= f.text_field :login %>

    # app/views/devise/registrations/new.html.erb

    <%= f.label :username %>
    <%= f.text_field :username %>

    Login from anywhere on the site

    Add a login form that posts to the user_sessions_path

     
    <%= form_for(:user, :url => user_session_path) do |f| %>
        <%= f.label :email %>
        <%= f.text_field :email %>
    
        <%= f.label :password %>
        <%= f.password_field :password %>
    
        <%= f.submit "Sign in" %>
    <% end %>
                

    Questions?

    Those Helpful Links Again

    Devise: http://github.com/plataformate/devise

    Slides: http://presentations.royvandewater.com/?file=devise.html

    Project on Github: http://github.com/royvandewater/devise-samples