ADR 005: Autenticación y oauth2-server¶
Estado¶
Aceptado — 2026-06 · Diseño de ingeniería (sin implementación aún)
Relacionado con ADR 004 (PanelAuthPolicy), ADR 001 y taxonomía Dependiente en contexto-proyecto.md.
Contexto¶
El marco técnico exige OAuth2/OIDC, JWT, ACL y permisos por panel. La pizarra de arquitectura ubica esto en el módulo interno oauth2-server (equivalente funcional a Keycloak embebido para las aplicaciones Cortex).
Decisión de producto: modo interno por defecto (CORTEX_AUTH_MODE=internal), con opción de delegar en un IdP externo (CORTEX_AUTH_MODE=external) para despliegues que ya operan Keycloak, Auth0 u otro.
Hoy CORTEX_JWT_SECRET existe en settings pero no hay middleware ni servidor OAuth en cortex_framework. El panel Control tiene placeholder de ACL sin enforcement.
Decisión¶
Módulo oauth2-server (Dependiente)¶
Paquete objetivo: cortex_framework.oauth2_server (nombre en código; producto: oauth2-server).
| Responsabilidad | Descripción |
|---|---|
| Emisión de tokens | Authorization Code + PKCE, client credentials (servicio a servicio) |
| Validación JWT | Access tokens en cada request API y MCP |
| ACL / permisos | Roles, scopes, mapeo a PanelAuthPolicy |
| Usuarios y clientes | Registro de apps (SPA, agentes, integraciones) — persistencia en PG del tenant o BD de plataforma según fase |
Modo interno (CORTEX_AUTH_MODE=internal)¶
- Cortex expone endpoints OAuth2/OIDC estándar (
.well-known/openid-configuration, authorize, token, JWKS). - Las demos
framework/web-shadcnusan PKCE contra el servidor embebido. - JWT firmados con claves rotables del módulo (no
CORTEX_JWT_SECRETad hoc en producción).
Modo externo (CORTEX_AUTH_MODE=external)¶
- Issuer configurable:
CORTEX_OIDC_ISSUER. - Audience:
CORTEX_OIDC_AUDIENCE. - Validación de firma vía JWKS del issuer (cache con TTL).
- Cortex no emite tokens; solo valida y autoriza.
Claims mínimos¶
| Claim | Uso |
|---|---|
sub | Identidad del usuario |
tenant_id o claim custom mapeado | Tenant de negocio |
scope / roles | Autorización de panels y rutas |
Reglas de tenant¶
- Header
X-Tenant-Idsigue siendo el contrato HTTP de la plataforma. - Si ambos están presentes (header y claim), deben coincidir; si no,
403 TENANT_MISMATCH. - Si solo hay claim, el middleware deriva el tenant desde el token.
- Rutas públicas:
/api/v1/health,/api/v1/ready,/api/docs,/api/openapi.json, discovery OAuth (modo interno).
Middleware y guards¶
Orden en create_app:
TenantMiddleware(existente)OidcAuthMiddleware— valida Bearer (interno o externo según modo), poblarequest.state.usery tenant efectivoRateLimitMiddleware(ADR 009)PanelScopeGuard— evalúaPanelAuthPolicy.scopescontra claims
sequenceDiagram
participant U as Usuario
participant Auth as oauth2-server
participant Web as framework/web-shadcn
participant API as FastAPI apirest
participant PLG as cortex_plugin_*
U->>Auth: login authorize PKCE
Auth-->>Web: access_token
Web->>API: Bearer + X-Tenant-Id
API->>API: validar JWT y claims
API->>PLG: request autorizado Leyenda: Flujo de login y API autorizada. Actores: usuario, módulo oauth2-server (interno o validación externa), SPA demo, capa apirest, plugin de negocio. Estado: Diseño.
flowchart TB
subgraph oauth [oauth2-server - Dependiente]
SRV[Servidor OAuth2 / validador JWT]
ACL[ACL y PanelScopeGuard]
end
subgraph io [cortex_framework.io]
MW[TenantMiddleware]
API[apirest]
end
Token[Bearer JWT] --> SRV
Header[X-Tenant-Id] --> MW
SRV --> ACL
ACL --> API Leyenda: Módulos internos en la cadena de una petición autenticada. Actores: cliente HTTP con JWT y tenant header. Estado: Diseño (middleware tenant parcialmente implementado).
Frontend¶
@cortex/panel-core:PanelApiClientaceptagetAccessToken(): Promise<string | null>.createPanelShellApprecibe provider de token (cliente OAuth interno o externo).- El SPA adjunta
Authorizationen cadafetch.
Errores estandarizados¶
| Código | HTTP | Cuándo |
|---|---|---|
UNAUTHORIZED | 401 | Token ausente o inválido |
FORBIDDEN | 403 | Scope insuficiente o tenant mismatch |
TOKEN_EXPIRED | 401 | exp vencido |
Usar ApiError en cortex_framework/api/errors.py.
Fuera de alcance fase 1¶
- Refresh token en SPA (documentar PKCE puro o BFF con cookie httpOnly).
- RBAC granular completo del panel Control (fase 2).
- Federación social (Google, etc.) — solo OIDC estándar.
Consecuencias¶
- Positivas: self-hosted sin Keycloak obligatorio; flexibilidad enterprise con IdP externo; un solo módulo Dependiente para identidad.
- Negativas: el modo interno aumenta superficie de seguridad a mantener; tests requieren mock JWKS o servidor de dev.
Referencias¶
core/cortex_core/spi.py—PanelAuthPolicyframework/cortex_framework/settings.pyframework/cortex_framework/tenant/middleware.py- ADR 010 — capa
apirestymcp