Devise: Invalidating all sessions for a user

Background information about session storage in Rails

Rails has a default mechanism to store the session in the CookieStore. This is a cookie which holds the entire user session hash in the browser. This cookie is serialized, encoded with base64, and signed.

How Devise handles authentication

Devise uses this CookieStore. To track a users session, a salt is stored in the session cookie when a user logs in.
When a user logs out this CookieStore is overwritten and current_user is set to nil.

But what about the old session cookie?

This session cookie is still valid! If this cookie is stolen before it gets overwritten, it can be reused to get access to the application again.

How can we invalidate old session cookies on logout with Devise?

1) Reset password

The only option provided per default by Devise is to change the password of a user. The salt stored in the session cookie by Devise relies on the password. So if you want to logout a user AND invalidate old session cookies you need to reset her password.

2) Extend the salt with a token

Add a session_token column to your devise model (e.g. User) and override Devise #authenticatable_salt method to contain your session token:

class User < ApplicationRecord

  devise :database_authenticatable, :recoverable, :rememberable
         
  def authenticatable_salt
    "#{super}#{session_token}"
  end

  def invalidate_all_sessions!
    update_attribute(:session_token, SecureRandom.hex)
  end
  
  ...
end

You can now invalidate the session cookie by resetting the session_token of a user, when she logs out:

class Users::SessionsController < Devise::SessionsController
  
  def destroy
    current_user.invalidate_all_sessions!
    super
  end

end

Note that changing the session_token will invalidate all session cookies of this user. If she is logged in on other devises (e.g. tablet, mobile phone, ...) she will be logged out on all of them.

3) Switch to a persisted storage for sessions on the server

Another option to prevent this session replay attack is to switch to persisted storage for sessions. Therefore you can use either a database, memcache or redis. (e.g. with ActiveRecord::SessionStore)
But this means, you need to take care of adding, accessing, and removing the session data. This may has an impact on performance on high traffic sites since a session may be allocated even for anonymous browsing traffic.

Natalie Zeumann about 3 years ago
This website uses short-lived cookies to improve usability.
Accept or learn more