Idioma: English Español
Bounded Context: Identity (Ums.Domain.Identity)
Propietario: AuthMethodResolverService (capa Application)
Estado: Producción
UMS soporta dos métodos de autenticación por tenant, pero no se aplican de forma uniforme a todos los puntos de entrada:
| Método | Descripción |
|---|---|
| Local | Validación de credencial BCrypt. Hash de contraseña almacenado en la entidad PasswordCredential. Se usa principalmente para el acceso de gestión del portal. |
| IDP | Autenticación federada delegada a un Proveedor de Identidad externo (Azure AD, Okta, SAML2, etc.). Se usa principalmente para la autenticación de la API externa. |
El método aplicable para un tenant dado se resuelve en tiempo de login desde configuración, nunca está hardcodeado. El resolver también recibe un AuthAccessScope para que el flujo de gestión del portal pueda permanecer local mientras el flujo de API externa sigue respetando la configuración IDP del tenant. Esto significa que el método de autenticación puede cambiar sin redesplegar la aplicación.
El parámetro AUTH_USE_EXTERNAL_IDP (Booleano, con scope de tenant) gobierna la selección del método solo para el scope de API externa:
| Valor | Resultado |
|---|---|
false |
AuthMethod.Local() — validación BCrypt |
true + IDP activo |
AuthMethod.Idp(activeProvider) — autenticación federada |
true + sin IDP activo |
Result.Failure("AUTH_011") — error de configuración |
Para AuthAccessScope.PortalManagement, el resolver siempre devuelve AuthMethod.Local() y no requiere la configuración IDP del tenant.
La frontera de autorizacion que mantiene la gestion del portal en modo local esta definida en ADR-0077.
Este parámetro reside en el ParameterCatalog y se carga en IConfigurationProvider al iniciar la aplicación. Los cambios aplicados mediante la UI del IdpPanel se persisten en la base de datos y disparan un refresco del proveedor — el nuevo método toma efecto en el siguiente login sin reiniciar el servicio.
LoginCommand recibido
│
▼
IAuthMethodResolver.ResolveAsync(tenantId, scope)
│
├─ lee AUTH_USE_EXTERNAL_IDP desde IConfigurationProvider (en memoria)
│
├── false → AuthMethod.Local
│ │
│ └─► ILocalAuthStrategy.AuthenticateAsync(email, password)
│ └─ BCrypt.Verify(hash, password)
│
└── true → lee IdentityProvider activo desde el aggregate del tenant
│
├── ninguno → Result.Failure("AUTH_011")
│
└── encontrado → AuthMethod.Idp(provider)
│
└─► IIdpAuthStrategy.AuthenticateAsync(provider, credentials)
└─ Shell.Factory resuelve IIdpAuthAdapter por nombre de estrategia
└─ adapter.AuthenticateAsync(credentials)
IAuthMethodResolverServicio de aplicación puro. Lee IConfigurationProvider — sin consulta a BD por request.
Task<Result<AuthMethod>> ResolveAsync(Guid tenantId, AuthAccessScope scope, CancellationToken ct);
ILocalAuthStrategyValida credenciales BCrypt contra la PasswordCredential activa.
Task<Result<AuthenticatedPrincipal>> AuthenticateAsync(
string email, string password, Guid tenantId, CancellationToken ct);
IIdpAuthStrategyDespacha al IIdpAuthAdapter correcto por el campo Strategy del proveedor (ej. AZURE_AD, OKTA, SAML2, GENERIC_OIDC). Resuelto vía Shell.Factory.
Task<Result<ExternalIdentity>> AuthenticateAsync(
IdentityProvider provider, string credentials, CancellationToken ct);
IIdpAuthAdapter (Puerto ACL — capa Infrastructure)Una implementación por tipo de IDP. Registrado en IdpAuthAdapterFactorySetup.
string StrategyName { get; } // coincide con el valor de IdpStrategyHint
Task<Result<ExternalIdentity>> AuthenticateAsync(
IdentityProvider provider, string credentials, CancellationToken ct);
IConfigurationProvider combina parámetros de dos niveles:
| Nivel | Alcance | Precedencia |
|---|---|---|
| Default global | TenantId = NULL (raíz) |
Menor |
| Override por tenant | TenantId = <específico> |
Mayor |
Cuando un tenant no tiene un override explícito de AUTH_USE_EXTERNAL_IDP, aplica el default global. Esto significa que un default a nivel de plataforma (por ejemplo, autenticación Local) toma efecto para todos los tenants nuevos sin configuración manual, y los tenants individuales pueden sobreescribirlo de forma independiente.
IConfigurationProvider se pobla una vez al inicio (o al refresco explícito) y sirve las búsquedas posteriores como lecturas O(1) en diccionario. El resolver por lo tanto agrega latencia cero por request de autenticación más allá de la lógica de capa de aplicación.
Cuando el toggle del IdpPanel dispara UpdateAuthModeCommand, el command handler:
IConfigurationProvider.RefreshAsync() para re-poblar la caché en memoriaStubIdpAuthAdapter se registra en entornos no-Productivos. Acepta cualquier credencial prefijada con MOCK- y retorna una ExternalIdentity fabricada que coincide con el usuario UMS por sufijo de email. Esto permite pruebas de extremo a extremo del flujo IDP sin un proveedor externo real.
Después de que el método de autenticación se resuelve y el usuario es autenticado, AuthorizationGraphBuilderService construye el AuthorizationGraph completo. La sección authentication del grafo registra:
Local o IDP)mfaRequired, issuedAt, sessionExpiresAtVer Grafo de Autorización para la estructura completa del grafo.
| Código | Disparador |
|---|---|
| AUTH_001 | Error de validación — campos requeridos faltantes |
| AUTH_002 | Tenant no encontrado |
| AUTH_003 | Tenant no activo |
| AUTH_004 | Usuario IDP sin UserAccount correspondiente en UMS |
| AUTH_005 | UserAccount.Status != ACTIVE |
| AUTH_006 | Credenciales inválidas (BCrypt Local) |
| AUTH_011 | AUTH_USE_EXTERNAL_IDP = true pero sin IDP activo configurado |
| AUTH_012 | Sin IIdpAuthAdapter registrado para el nombre de estrategia del proveedor |