Cómo hacer logout en varias aplicaciones Rails a la vez

Es muy común encontrar esta pregunta en los foros, sobre todo relacionándola con Doorkeeper y OAuth en concreto, pero las gestión de sesiones está totalmente fuera del área de alcance del protocolo OAuth y por ende de Doorkeeper.

Un escenario habitual es tener una aplicación que hace de proveedor OAuth y otra cliente, cada una con su propio sistema de sesiones de usuario, aunque en el cliente se genere el usuario a partir de los datos que obtenemos gracias al proveedor OAuth.

Lo que tendrémos que hacer en este escenario es que cuando se haga logout en cualquiera de las dos aplicaciones se borre la sesión de ambas aplicaciones.

Para ello tendremos que hacer que ambas aplicaciones puedan borrar las sesiones entre ellas:

# config/initializers/session_store.rb

# Para la apllicación provider
Rails.application.config.session_store :cookie_store, key: '_provider_session', :domain => ENV['SESSION_DOMAIN']

# Para la aplicación cliente
Rails.application.config.session_store :cookie_store, key: '_client_session', :domain => ENV['SESSION_DOMAIN']

En mi caso he usado el dominio .lvh.me porque es muy códomo en desarrollo, obviamente tendremos que acceder a las aplicaciones por esos subdominios, por ejemplo provider.lvh.me:3000 y client.lvh.me:3001.

Lo mejor es meter el dominio en una variable de entorno para que sea mas configurable, así cada developer puede acceder a la aplicación en local con el dominio que prefiera y se podrá desplegar a producción sin problemas.

A la hora de hacer logout en cada aplicación hay que eliminar la cookie de la otra aplicación y la nuestra propia.

Así es como podríamos hacerlo con Devise:

## Para el provider app

# app/controllers/users/sessions_controller.rb
class Users::SessionsController < Devise::SessionsController
  def destroy
    cookies.delete(:_client_session, domain: ENV['SESSION_DOMAIN'])
    super
  end
end

## Para el client app
class Users::SessionsController < Devise::SessionsController
  def destroy
    cookies.delete(:_client_session, domain: ENV['SESSION_DOMAIN'])
    super
  end
end

Comentarios