Tutorial: Casual Authentication in Rails

My latest site uses, what I call, “Casual Authentication“. It’s best used for sites where the risk of an intruder getting in to a user’s account isn’t huge and is of little value to an intruder. For example, a to do list, or other small web app without sensitive data. The basic way it works is that:

  1. The users signs up with one click. This takes them to a personalised page.
  2. At this point a unique 7-digit Key is generated and assigned to the user
  3. The Key is then used by the user to log in to their page, either by entering their key in a textbox on your home page, or by routing URLS, for example:

www.foosite.com/6837349 will log the user in if their key is 6837349

The main advantage of this approach is that it is quick to sign up users and quick for them to sign in on return visits. If required, you could introduce another level of authentication, for example, asking for their name or e-mail address after they’ve entered their key.

Here’s how to implement the basic functionality.

 

The User Model

To prepare our User model to support casual authentication, add the following methods:

class User < ActiveRecord::Base

  validates_presence_of :access_key
  validates_uniqueness_of :access_key 

  # override superclass initialize so that a new
  # access_key is always generated for a user
  def initialize()
    super()
    self.access_key = User.generateKey
  end

  # used to sign a user in
  def self.authenticate(key)
    # find the user with this key
    u=find(:first, :conditions=>["access_key = ?", key])
    return nil if u.nil?
    return u
    nil
  end  

  def self.generateKey
    # key must be between
    @minKey = 1000000;
    # and
    @maxKey = 9999999

    # now we generate a random number between minimum and maximum
    @potentialKey = @minKey + rand(@maxKey - @minKey)

    # check key not in use by another user
    # if so generate a new key recursively until a
    # unique one is found
    if User.find(:first, :conditions => ["id = ?", @potentialKey])
      @potentialKey = self.generateKey
    end

    return @potentialKey
  end 

  # other User model logic...

end

Note that the key here is a 7-digit number between 1,000,000 and 9,999,999. Obviously to decrease the chance of someone finding an active account, or to reduce the risk of collisons further, you can increase the upper bound (@maxKey) of your key set. 

The migration for the User model is as follows:

class CreateUsers < ActiveRecord::Migration
  def self.up
    create_table :users do |t|
      t.column :access_key, :integer
      # other fields for your user model...
    end
  end

  def self.down
    drop_table :users
  end
end

You only need the access key field, as well as any other fields you store about your users, such as their name or location.

 

The User Controller

Here are the signup and signin methods in the User controller:

class UserController < ApplicationController

  # signs up a new user
  def signup
    @user = User.new()
    # save the user to db
    if @user.save
      # sign in the new user
      session[:user] = User.authenticate(@user.access_key)

      # confirmation and instructions to the user that sign up was successful
      flash[:message] = "Signup successful! Your key is <b>" << @user.access_key.to_s << \
        "</b>, make sure you note it down! <br/>You can access the site directly <b>"    \
        "<a href='http://www.foosite.com/" << @user.access_key.to_s << "'>"              \
        "by bookmarking this link</a></b>"

      # signed up so take to their home page
      redirect_to :controller => "user", :action => "home"
    else
      logger.warn("Saving new user failed")
      flash[:warning] = "Signup unsuccessful"
      # either redirect_to_stored or to some other page
      redirect_to :action => 'front'
    end
  end

  # action called to sign a user in
  def signin
    if session[:user] = User.authenticate(params[:access_key])
      flash[:message]  = "Login successful"
      redirect_to :controller => 'user', :action => 'home'
    else
      flash[:warning] = "Login unsuccessful"
    end
  end
end

 

Signing up new users

It’s easy, trust me. Just add this where needed:



<%= link_to 'Click here to create an account', :controller => 'user', :action => 'signup' %>

 

Signing In Users 

They can in sign in via www.foosite.com/[theirkey], you can implement this by adding the following to your routes.rb file:

ActionController::Routing::Routes.draw do |map|

     map.connect ':access_key', :controller => 'user', :action => 'signin'
     # rest of your routes...

Or by providing a text box that they enter their key in:

    <% form_tag :controller => 'user', :action => 'signin' do %>
        <br/>
        <p><label for="user_login">Key:</label><br/>
        <%= text_field_tag('access_key', '', :size => '8', :maxlength => '8') %></p>
        <br/>

        <p><%= submit_tag "Sign in" %></p><br/>

        <%= link_to 'Click here to create an account', :controller => 'user', :action => 'signup' %>
    <% end %>

 

The Usual Stuff

You probably want to introduce some logic so that users who are already signed in don’t get your “Click here to create an account” link and, instead, see some sort of other links.

You can check if a user is signed in from anywhere in your application by adding the current_user helper below.

There is also a helper to retrieve the current user object, and a further helper to derive the URL for the user to bookmark to access their account directly:

class ApplicationController < ActionController::Base

  # return the current user
  helper_method :current_user
  def current_user
    session[:user]
  end

  helper_method :logged_in?
  def logged_in?
    return session[:user] ? true : false
  end

  # the users key to enter the site
  helper_method :get_bookmark
  def get_bookmark
    return 'www.foosite.com/' << current_user.access_key.to_s
  end

end

 

The End

Thats it. I hope that works for you as a quick, easy and convenient method of casual user authentication. Let me know if you have an comments or suggestions.

~ by Alex on September 22, 2008.

One Response to “Tutorial: Casual Authentication in Rails”

  1. Whatever methods we use to teach skills and knowledge at the professional stage involve us in many assumptions about how we learn professional skills, knowledge and values, what we learn and why, and what we expect students and trainees to do with that learning. ,

Leave a Reply