I'm building a Rails based application for a client, which they are in-turn licensing to their customers on a per-seat basis. This means that they don't want multiple people using the same login at the same time; if you have 5 people in your shop, you have to purchase 5 licenses. Makes sense to me.

Anyway, we're using the splendid Authlogic plug-in for all the authentication duties and while it does many things very well it doesn't have a built-in way to restrict simultaneous logins. I asked around and got a couple of tips on what might work and how I might proceed. It was a simple process but after several others had the same problem I thought I would formalize the "solution" in a blog post in hopes that it might help someone else in the future.

  • Step One - Add a place to store a session key
    # add_session_key_to_users.rb
    class AddSessionKeyToUsers < ActiveRecord::Migration
    def self.up
    add_column :users, :session_key, :string
    end

    def self.down
    remove_column :users, :session_key
    end
    end
    This gives us a place to store a "session_key" value that changes every time a user logs in.
  • Step Two - Insert the "session_key" on login
    # user_sessions_controller.rb
    def create
    ...
    if @user_session.save
    # Save the session ID to detect simultaneous login attempts
    @user_session.record.session_key = session[:session_id]
    @user_session.record.save!
    ...
    end
    end

    What this does is forces the session_id to be saved to the User model each time a user logs into the site. We'll check this value later to make sure the session hasn't changed. I'm using the session_id here as a "unique" value but it could be anything you want; timestamp, IP address, etc...
  • Step Three - Make sure the user is unique
    # application_controller.rb
    def current_user
    ...
    # Prevent simultaneous logins
    if @current_user && @current_user.session_key != session[:session_id]
    flash[:notice] = 'Access denied. Simultaneous logins detected.'
    current_user_session.destroy
    end
    end

    Here is where the rubber meets the road, so to speak. In Authlogic, the current_user method is accessed on every page request so it is the perfect place to check for duplicate user sessions. We simple verify that the session_id in the users cookie is the same one in the database. If they are different we destroy the session and update the flash message.


This is a pretty simple little hack but it seems to work OK. One "problem" that I have noticed is that while the session is immediately destroyed the current request continues unabated. This means that the two users could possibly perform two actions at the same time, but it shouldn't be a problem. Another side effect of this technique is that the last user to log in always gets the session. This might be a problem for you but it wasn't for my project.