Saltar a contenido

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-shadcn usan PKCE contra el servidor embebido.
  • JWT firmados con claves rotables del módulo (no CORTEX_JWT_SECRET ad 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-Id sigue 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:

  1. TenantMiddleware (existente)
  2. OidcAuthMiddleware — valida Bearer (interno o externo según modo), pobla request.state.user y tenant efectivo
  3. RateLimitMiddleware (ADR 009)
  4. PanelScopeGuard — evalúa PanelAuthPolicy.scopes contra 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: PanelApiClient acepta getAccessToken(): Promise<string | null>.
  • createPanelShellApp recibe provider de token (cliente OAuth interno o externo).
  • El SPA adjunta Authorization en cada fetch.

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.pyPanelAuthPolicy
  • framework/cortex_framework/settings.py
  • framework/cortex_framework/tenant/middleware.py
  • ADR 010 — capa apirest y mcp