Estado: Aceptado Fecha: 2026-05-31 Responsable de Decisión: Arquitectura Relacionados:
El objetivo principal de UMS es actuar como el motor central de autenticación y autorización para los sistemas cliente multi-tenant. Antes de esta decisión, el endpoint de login (POST /api/v1/auth/login) retornaba un array vacío Permissions: [] y realizaba únicamente validación BCrypt. Los sistemas cliente recibían un JWT sin datos de autorización accionables, obligándolos a implementar su propia lógica de permisos o re-consultar UMS en cada request.
El sistema necesita:
Tras la autenticación, UMS construye un registro AuthorizationGraph que contiene:
AuthorizationGraph
├── context — usuario, tenant, systemSuite, rol, profile, branch
├── authentication — método (Local|IDP), proveedor, mfaRequired, expiración
├── actions[] — todas las acciones registradas en el SystemSuite
├── menuAccess[] — árbol Módulo→Menú→SubMenú→Opción con AccessEffect por opción
├── domainPermissions[] — recursos de dominio con efecto por acción (Aggregate/Entity)
├── featureFlags[] — flags evaluados contra el contexto del usuario al momento de autenticación
├── effectiveConfig — parámetros resueltos por tenant (timeout de sesión, MFA, etc.)
├── scopes[] — scopes OAuth2-style "resourceCode.actionCode" desde permisos Allow
├── generatedAt — timestamp UTC
└── validUntil — generatedAt + SESSION_TIMEOUT_MINUTES
El grafo es inmutable tras su construcción. Los sistemas cliente lo cachean durante la vigencia de la sesión y toman decisiones de autorización localmente.
Para cada trío (TargetType, TargetId, ActionId):
ProfilePermission donde IsActive = trueIsOverride = true → usar valores del PP (Source = Override)IsOverride = false → usar valores originales del TemplateItem (Source = Template)IsDenied = true → AccessEffect.Deny (siempre gana sobre Allow)IsAllowed = true → AccessEffect.AllowAccessEffect.NotGranted (denegación implícita)POST /api/v1/auth/login — endpoint existente, ahora retorna el grafo completo. Establece cookie de sesión para el frontend web (compatible con versiones anteriores). Retorna LoginSuccessResponse enriquecido con AuthorizationGraph.POST /api/v1/client/authenticate — nuevo endpoint stateless para sistemas cliente externos. Sin cookie. Retorna ClientAuthResponse con JWT + grafo serializado.El grafo se serializa en el formato configurado por el tenant (parámetro AUTH_GRAPH_DEFAULT_FORMAT, default: JSON). Formatos soportados: JSON, XML, YAML, CSV.
Los clientes pueden sobrescribir mediante el query param ?format=xml o el header Accept. Nuevos formatos se agregan registrando una implementación de IAuthorizationGraphSerializer en AuthorizationGraphSerializerFactorySetup — sin cambios en el código de aplicación.
Los sistemas cliente necesitan verificar permisos múltiples veces por request (renderizado de menús, visibilidad de botones, guardas de acceso a API). Un grafo autocontenido elimina N round-trips a UMS por sesión. El grafo es válido por SESSION_TIMEOUT_MINUTES — la misma duración que el token de sesión.
Los claims JWT tienen un límite de tamaño y son estáticos después de su emisión. El grafo es rico (cientos de permisos), necesita incluir datos dinámicos (feature flags) y debe reflejar el estado en el momento de la autenticación. El grafo se serializa por separado y se embebe en el body de la respuesta, no en el JWT (el JWT solo lleva claims de resumen compacto).
Ums.Domain.Enums.PermissionEffect ya existe como DomainEnumeration con Allow y Deny. El grafo necesita un tercer valor NotGranted (denegación implícita — no existe entrada de permiso). Un nuevo enum AccessEffect en el namespace del grafo evita extender la enumeración existente.
| Componente | Ubicación |
|---|---|
Registro AuthorizationGraph |
Ums.Domain/Authorization/Graph/ |
Puerto IAuthorizationGraphBuilder |
Ums.Domain/Authorization/Graph/IAuthorizationGraphBuilder.cs |
AuthorizationGraphBuilderService |
Ums.Application/Authorization/Graph/AuthorizationGraphBuilderService.cs |
IAuthorizationGraphSerializer |
Ums.Application/Authorization/Graph/Serializers/ |
| Serializadores JSON/XML/YAML/CSV | Ums.Infrastructure/Authorization/Graph/ |
AuthorizationGraphSerializerFactorySetup |
Ums.Infrastructure/Authorization/Graph/ |
AuthGraphFormatProvider |
Ums.Application/Authorization/Graph/AuthGraphFormatProvider.cs |
POST /api/v1/auth/login |
Ums.Presentation/Endpoints/Identity/Auth/AuthEndpoints.cs |
POST /api/v1/client/authenticate |
Ums.Presentation/Endpoints/Identity/Auth/ClientAuthEndpoints.cs |
GET /api/v1/profiles/{id}/auth-graph/preview |
Ums.Presentation/Endpoints/Authorization/Profile/ProfileEndpoints.cs |
El endpoint GET /api/v1/profiles/{profileId}/auth-graph/preview usa el mismo pipeline IAuthorizationGraphBuilder que el flujo externo POST /client/authenticate. La validación de credenciales es el único paso omitido — el perfil se resuelve directamente por ID.
Este diseño garantiza que los administradores vean exactamente el grafo que se produciría para una solicitud de autenticación real. Ver ADR-0080: Previsualización del Auth Graph — Pipeline Interno vs Externo para la justificación completa.
Diferencias clave de comportamiento respecto al flujo externo:
IUserContext.IsAuthenticated).Graph.Preview.Internal en lugar de Auth.Success.X-Preview-Mode: internal-preview y X-Request-Id.?format= para override.