/ ruby

Dead Simple Duplicate Session Checking In Rails

Many apps that you will write in your programming career may allow for multiple sessions and while your use case may vary it's fine for most cases especially when it's an application with a mix of components (web/mobile/desktop). From a user experience you'd obviously not want to invalidate user sessions as that would be annoying.

But let's just say for arguments sake you have an application that has a more strict security requirement and you want to ensure that a single user cannot be logged in and using your system from multiple computers/devices. There are a few Rubygems out there that do this, however I found that by leveraging a few simple techniques this can be done in a few lines of code and it works well.

This simple tutorial assumes that you are using Devise for authentication, however this technique could be applied to other plugins or your own authentication. Our goal here is to evaluate the current_user's (handy method Devise gives us to get the current user in the request cycle) session token and kick them out if there's a difference in tokens.

Since this is an application-wide technique, the most likely place to put this code is in app/controllers/application_controller.rb. Let's go ahead and write a method called duplicate_session? to eval the session token:

  def duplicate_session?
    user_signed_in? && (current_user.login_token != session[:token])
  end

Let's break this down:

  • user_signed_in? is a method Devise gives us to eval if the user is currently signed into the application.
  • current_user.login_token is the token assigned when you login that is stored in your User (this name may vary by your choice) model.
  • The next part we eval using the Ruby operator != or (not equal to) to look for equality between the login_token and the session[:token] parameter.

Now we've got a simple setup which will let us write our next method, check_concurrent_session. This will also live in the app/controllers/application_controller.rb file. Let's go ahead and write it:

  def check_concurrent_session
    if duplicate_session?
      sign_out_and_redirect(current_user)
      flash[:notice] = "Duplicate Login Detected"
    end
  end

Again, let's break this down.

We are using a simple conditional if to evaluate our previous method duplicate_session?. If this condition is met, then we use the devise method of sign_out_and_redirect passing the current_user object to tear down the session, then finally displaying a flash notice with Duplicate Login Detected. Obviously, you can make this message whatever you want it to be, but this is what I chose.

We're almost done... hang in there. One more simple thing to do.

We're going to need a way to execute the check_concurrent_session method in each request cycle. So we'll use a callback, in this case a before_action which will execute our code before any request is allowed to process. This is as simple as putting the following line at the top of your app/controllers/application_controller.rb file.

before_action :check_concurrent_session

And literally, that's all there is to it. Go ahead and try logging in with your browser of choice (i.e. Chrome). Ok, logged in? Now open an incognito window and try to login to your app again. Ok logged in? Now go back to your original browser window and refresh the page. You're now signed out with a nice, (hopefully friendly) notice message.

Voila... we are done!

Dead simple and it works every time!