Seguridad e identidad
Identidad multiempresa sobre un proveedor de identidad extendido
Cómo dar acceso a usuarios de muchas empresas distintas, con identidades que no son únicas globalmente y una base de usuarios heredada que no podía cambiar de contraseña.
Contexto
Plataforma de facturación electrónica usada por muchas empresas distintas. Cada empresa tiene sus propios usuarios, sus propios productos contratados y su propia identidad visual. Había además una base de usuarios heredada de la plataforma anterior.
Problema
- El identificador de un usuario no es único globalmente: el mismo nombre de usuario puede existir en dos empresas distintas, así que el login necesita el identificador fiscal de la empresa como parte de la identidad.
- Los usuarios heredados tenían contraseñas guardadas con hashes antiguos, con capas adicionales de codificación. Pedir a todos un reset era inaceptable para el negocio.
- Cada empresa esperaba ver su logo y su aviso de privacidad en la pantalla de acceso.
Mi rol
Autor de las dos librerías de extensión y del repositorio de temas; único contribuyente en tres de las cuatro piezas.
Solución
- Un Authenticator SPI compone la identidad interna a partir de empresa + usuario y valida el identificador fiscal (formato y pertenencia).
- Si el usuario todavía no existe en el proveedor de identidad, un verificador propio comprueba la credencial heredada con comparación en tiempo constante y, si es válida, lo aprovisiona en ese mismo login con sus roles y grupos derivados de los productos contratados.
- La migración del resto de la empresa se lanza en hilos virtuales fuera del hilo de petición, para no penalizar el tiempo de login.
- Un FormAction SPI añade el preregistro de usuarios secundarios con validación de empresa y de duplicados.
- Un EmailSenderProvider SPI hace que los correos del proveedor de identidad salgan por el servicio de correo de la plataforma: una sola plantilla, un solo remitente, un solo log.
- Dos temas de login en FreeMarker resuelven el logo por dominio, con enrolamiento TOTP por QR y modo manual, selección entre varios dispositivos OTP, 9 idiomas y atributos de accesibilidad.
Decisiones y contrapartidas
- Extender el proveedor de identidad en vez de construir autenticación propia
- Se hereda gestión de sesiones, tokens, MFA y consentimiento. El coste es quedar atado al ciclo de vida de sus SPI: cada actualización mayor obliga a revisar interfaces.
- Migración perezosa al primer login, no masiva
- Nadie pierde el acceso y no hay ventana de mantenimiento; a cambio hay que mantener el camino heredado durante toda la transición y decidir cuándo apagarlo.
- Comparación en tiempo constante aunque el hash sea antiguo
- El algoritmo heredado es débil por definición, pero eso no justifica filtrar información por el tiempo de respuesta.
- Delegar el correo del proveedor de identidad a un servicio propio
- Unifica plantillas y trazabilidad, pero convierte al servicio de correo en dependencia del flujo de recuperación de contraseña.
Resultado
El acceso de todas las empresas pasa por una sola pieza extensible y los usuarios heredados entraron sin cambiar su contraseña.
Qué haría distinto
Cubrir las extensiones con tests de integración sobre un proveedor de identidad real en contenedor, en lugar de solo pruebas unitarias; y definir desde el principio la fecha de apagado del camino heredado, con una métrica que cuente cuántos logins siguen usándolo.
- Java 21
- Keycloak SPI
- FreeMarker
- JDBC multi-motor
- Virtual Threads
- OAuth2 / OIDC / JWT