Un solo login para siete apps y una sesión que se cerraba sola

Cómo comparten cuenta todas las apps del hub con Keycloak y un proxy de autenticación, el papel de cada tipo de token y el ajuste que hacía que el hub pidiera la contraseña cada poco.

El hub son siete apps y siete APIs, y ninguna de ellas sabe nada de contraseñas. Entras una vez y pasas de la lista de la compra al calendario sin volver a identificarte. Esto es lo que ocurre por debajo y la historia de un fallo desconcertante: la sesión se cerraba sola cada poco rato aunque, sobre el papel, duraba doce horas.

Sacar el login de las apps

La forma ingenua de hacerlo es que cada app gestione sus usuarios. Con siete apps eso son siete sitios donde guardar contraseñas y siete oportunidades de hacerlo mal. La alternativa es un servicio dedicado solo a la identidad. Aquí es Keycloak, un servidor de identidad de código abierto, que se ocupa de:

  • El registro, la verificación del email y la recuperación de contraseña.
  • Guardar las contraseñas como es debido, con un algoritmo de hash lento.
  • Bloquear temporalmente una cuenta tras varios intentos fallidos.
  • Mostrar las condiciones de uso y registrar cuándo las aceptó cada persona.

Las apps no ven nunca una contraseña. Reciben un documento firmado que dice quién eres.

El portero delante de todo

Las apps tampoco comprueban la sesión por su cuenta al cargar. Delante de todas ellas, en el proxy de entrada del servidor, hay un portero: cada petición que llega pasa primero por un servicio que mira si trae una sesión válida.

  • Si la trae, la petición sigue hacia la app, acompañada de un token que identifica al usuario.
  • Si no, el navegador es redirigido a la pantalla de inicio de sesión.

Así, una app nueva queda protegida por estar detrás del portero, sin escribir una línea de código de autenticación en ella.

Aun así, cada API comprueba el token

Sería un error fiarse solo del portero. Si alguien consiguiera llegar a una API por otro camino, esta aceptaría cualquier cosa. Por eso cada API verifica por sí misma la firma del token, quién lo emitió y para quién, y saca de ahí la identidad del usuario. Todas las consultas a la base de datos se filtran por esa identidad. Dos capas: el portero decide quién entra, y cada API vuelve a comprobar a quién sirve.

Tres relojes distintos

Aquí está la parte que causó el problema. En este esquema hay tres cosas que caducan, cada una a su ritmo:

QuéPara qué sirveCuánto dura
Token de accesoLo que reciben las APIs en cada petición5 minutos
Cookie de sesión del hubQue el portero te reconozcaHoras
Sesión en el servidor de identidadPermitir renovar el token sin pedir la contraseñaDepende de dos límites

El token de acceso dura poco a propósito: si alguien lo roba, le sirve cinco minutos. Para que eso no obligue a iniciar sesión cada cinco minutos, el portero lo renueva solo, en segundo plano, pidiéndole uno nuevo al servidor de identidad.

Primer tropiezo: renovar demasiado tarde

Al principio el portero renovaba el token cada treinta minutos. Como el token caducaba a los cinco, durante la mayor parte del tiempo las APIs recibían un token caducado y lo rechazaban todo, aunque el usuario tenía la sesión abierta. Se arregló renovando cada dos minutos, con margen bajo los cinco.

Segundo tropiezo: la sesión que moría de inactividad

Tiempo después llegó otra queja: «se cierra la sesión cada poco tiempo». La cookie duraba doce horas, así que no tenía sentido.

La causa estaba en el tercer reloj. La sesión en el servidor de identidad tiene dos límites: una duración máxima y un tiempo máximo de inactividad. El segundo estaba en su valor por defecto: treinta minutos.

Con la app abierta, el portero renueva el token cada dos minutos y eso cuenta como actividad. Pero en cuanto cierras el hub en el móvil, nadie renueva nada. Pasada media hora, el servidor de identidad da la sesión por abandonada. Cuando vuelves, la cookie del hub sigue siendo válida, el portero intenta renovar el token, el servidor de identidad contesta que esa sesión ya no existe, y acabas en la pantalla de inicio de sesión.

Para una app que se abre cinco veces al día durante un minuto, media hora de inactividad es el caso normal, no la excepción. Subimos los dos límites a veinticuatro horas, y la cookie del hub a lo mismo para que los tres relojes fueran coherentes.

Lo que no se relajó

Alargar la sesión es cómodo, pero no debe hacerse a costa del token de acceso. Ese sigue durando cinco minutos. Lo que dura un día es el permiso para renovarlo sin volver a escribir la contraseña. Y hay operaciones que piden la contraseña otra vez aunque la sesión esté abierta: para eliminar la cuenta, por ejemplo, el servidor de identidad exige volver a autenticarse justo antes.

Qué nos llevamos

  • Sacar el login a un servicio especializado ahorra escribir (y proteger) lo más delicado de cualquier aplicación.
  • Un portero delante de todo simplifica, pero cada API debe seguir verificando el token.
  • Cuando hay varios elementos que caducan, el que manda es el más corto de los que no se renuevan solos. En nuestro caso no era ni el token ni la cookie: era un límite de inactividad que nadie había mirado.
  • Los valores por defecto de estas herramientas están pensados para aplicaciones de oficina, donde alguien trabaja horas seguidas. Una app doméstica que se usa a ratos necesita otros.