Comment limiter l’accès à une seule session utilisateur à la fois, avec le Gem Devise ?

Il arrive parfois que dans des milieux très sensibles à la sécurité, comme la santé, il soit nécessaire de s’assurer qu’un utilisateur est connecté à votre application RoR une seule et unique fois.

Le principe est de générer un token unique à chaque nouvelle session et de l’associer à l’utilisateur. Si l’utilisateur tente de se connecter depuis un autre navigateur, c’est le dernier token généré qui sera alors associé à l’utilisateur. La session précédente sera détruite car le token sera obsolète.

Partant du principe que vous avez utilisé Devise pour gérer l’authentification, il vous est alors possible de contrôler qu’un utilisateur n’a qu’une session active en utilisant l’extension “devise_security_extension” (https://github.com/phatworx/devise_security_extension ) comme suit :

Ajoutez le gem à votre Gemfile

gem 'devise_security_extension'

Après avoir fait le rituel bundle install, lancez l’installation de l’extension :

rails g devise_security_extension:install

Lancez ensuite la migration

rails g migration AddSessionLimitableToUsers unique_session_id

Modifiez le fichier de migration pour limiter la taille du champ unique_session_id (and be a good citizen :)

class AddSessionLimitableToUsers < ActiveRecord::Migration
  def change
    add_column :users, :unique_session_id, :string, limit: 20
  end
end

Lancez la migration

rake db:migrate

Modifiez le model User afin d’ajouter l’option ‘ :session_limitable’ à la suite de vos options Devise

# app/models/user.rb
class User < ActiveRecord::Base
  devise :session_limitable # à la suite de vos options existantes et actives
  ... / ...
end

C’est terminé.

Si maintenant un utilisateur tente de se connecter depuis un autre navigateur, il en sera informé et sa précédente session sera fermée.

Bonus

Pour limiter la session d’un utilisateur dans le temps, il suffit d’ajouter l’option Devise :timeoutable et de définir le temps d’inactivité en ajoutant à votre model User avec la méthode suivante :

def timeout_in
     15.minutes
end