acts_as_authenticated_user

Rails user authentication plugin

Stars
11

acts_as_authenticated_user

acts_as_authenticated_user is a Rails user authentication plugin which is in many respects comparable to the similarly named acts_as_authenticated. Like acts_as_authenticated, it uses SHA1 hashes and salted passwords.

It differs from acts_as_authenticated in that it does not use generators (which leads to a User model cluttered with authentication logic). Instead, authentication logic is included automatically by calling an acts_ method (acts_as_authenticated despite its name, does not use any acts_ methods) within your user model.

Similarly, abstractions are provided for controllers to keep as much authentication logic 'under the hood'. See below for examples.

Installation

# script/plugin install git://github.com/spohlenz/acts_as_authenticated_user.git

Model Setup

The user model requires three fields: login, hashed_password and salt (all strings). This can be achieved with a migration such as:

class CreateUsers < ActiveRecord::Migration
  def self.up
    create_table :users do |t|
      t.string :login
      t.string :hashed_password
      t.string :salt
      
      ... your custom user fields ...
    end
  end
  
  def self.down
    drop_table :users
  end
end

The model can then be declared as an authenticated user with:

class User < ActiveRecord::Base
  acts_as_authenticated_user
end

Allowing the following behaviour:

u = User.create(:login => 'sam', :password => 'foobar', :password_confirmation => 'foobar')
User.authenticate('sam', 'foobar') #=> u
User.authenticate('invalid', 'user') #=> nil

If you want to use another field for your main identifier (e.g. email instead of login), replace login in your database schema and call acts_as_authenticated_user with :identifier => :email.

acts_as_authenticated_user will validate the presence of login and password but any extra validations will need to be defined in the User model. If you want to disable validations altogether (for example to define your own validation messages), declare acts_as_authenticated_user :validate => false in your User model. If you do this, be sure to define at least the following validations:

  • validates_presence_of {identifier_column}
  • validates_uniqueness_of {identifier_column}
  • validates_presence_of :password, :if => :password_required?
  • validates_presence_of :password_confirmation, :if => :password_required?
  • validates_confirmation_of :password

acts_as_authenticated_user does not define any validations as to the length of the password. You will need to specify these yourself.

Controller Setup

In your application_controller.rb:

class ApplicationController < ActionController::Base
  authenticated_user
end

Controllers and helpers have access to the logged_in? and current_user methods to determine the current login status.

Protecting a Controller

To protect a controller:

class PrivatePageController < ApplicationController
  require_login

  def index
  end
end

require_login takes an options hash accepting the same parameters as before_filter (e.g. :only and :except for per-action filtering). Other options include:

:with => a callable object (Proc, lambda or method) which must return true for the user to have access to that page or controller
:message => what to set flash[:error] to if the login check fails (defaults to 'Login required.')
:login_path => the location of the login path (defaults to /login) [use a symbol if specifying a named route]

An example combining all of these is:

class AdminController < ApplicationController
  require_login :only => [ :update, :create, :destroy ],
                :with => Proc.new { |u| u.is_admin? },
                :message => 'Admin privileges required',
                :login_path => :admin_login_path
end

Example (Handling login/logout)

To handle login/logout, create a controller to contain authentication actions:

class AccountController < ApplicationController
  def login
    process_login do |login|
      login.success { flash[:message] = 'Successful login' }
      login.failure { flash[:error] = 'Invalid login' }
    end
  end

  def logout
    process_logout do
      flash[:message] = 'Logged out'
    end
  end
end

The login action should respond to both GET and POST (a GET request will render the login form, a POST request will process the login). The login form should pass :login (or your custom identifier) and :password in the POST params.

The process_login method yields a login object which responds to success and failure, allowing you to define a block to execute in case of success or failure (before any redirection). process_login also takes an optional parameter - the location to redirect to after a successful login.

Any request to logout will clear the current session. process_logout takes a single argument - the location to redirect to after logging out (optional, defaults to '/'). It also accepts an optional block which will be called on logout.

Cookie login (Remember me)

acts_as_authenticated_user supports cookie-based logins as long as you have the fields remember_token (string) and remember_token_expires_at (datetime) in your users table. The remember token and cookie is automatically set in process_login if :remember_me is set to '1' in your params[:user] hash.

To Be Implemented

  • OpenID authentication

Copyright (c) 2008 Sam Pohlenz [[email protected]], released under the MIT license