Skip to main content
El IdP no es un servidor de autorización construido desde cero. Es un delgado facade de B Brands sobre un motor OAuth 2.1 / OIDC administrado, más la capa de identidad, habilitación, auditoría y ciclo de vida que el motor no provee.

1. Capas de componentes

El sistema se compone de cinco capas lógicas que viven en piezas físicas distintas.
Lo que el IdP NO hace: no define permisos de aplicación, no computa permisos efectivos, ni inyecta roles/permisos en los tokens. La autorización de grano fino vive en cada cliente; las APIs de B Brands autorizan con su RBAC interno existente.

2. Cómo se organiza el código

La lógica del IdP es un módulo transversal, separado del patrón estándar por recurso del resto de la API.

3. Decisiones arquitectónicas clave (ADR-lite)

Usamos un motor OAuth 2.1 administrado como servidor de autorización: ya es nuestra base de identidad, implementa el protocolo completo (PKCE, OIDC, JWKS, rotación de refresh), y los hooks de token de acceso personalizados nos permiten reescribir iss/aud y aplicar la habilitación. Contrapartida: dependemos de las capacidades del motor; la falta de scopes personalizados nativos es irrelevante porque no inyectamos permisos en los tokens.
Cada endpoint OAuth, OIDC, discovery y JWKS se sirve bajo un origen de B Brands. Los clientes nunca ven las URLs del motor, ni siquiera para el discovery o JWKS. Esto permite intercambiar el backend de identidad sin tocar la configuración del cliente, y habilita lógica personalizada (rate limiting, auditoría, transformación) en un único origen.
El claim iss de los tokens de acceso emitidos es la URL auth.* del entorno (p. ej. https://development.auth.bbrands.io en development), reescrita por el hook de token de acceso. Un token no productivo nunca es válido en producción porque los clientes validan el iss contra su propio entorno. Esto revisa la decisión original que nombraba a api.* como el issuer.
El origen auth.* sirve tanto las superficies interactivas (login, consentimiento) como cada endpoint del protocolo (discovery, JWKS, authorize, token, userinfo, revoke) — estos últimos mediante reescrituras transparentes al backend api.*. Esta es la forma de accounts.google.com: un origen de identidad estable para los clientes, mientras que el host de implementación detrás de él permanece reemplazable. Verificaciones de consistencia en el arranque y un monitor de disponibilidad de JWKS protegen el contrato.
Los JWT OAuth se firman con ES256 (ECC P-256); la clave pública se expone vía JWKS para que los clientes puedan validar sin compartir secretos. El JWT interno de B Brands sigue usando HS256 con un secreto del lado del servidor. Los dos formatos coexisten y nunca se mezclan.
Los clientes cuya librería OIDC solo valida RS256 (p. ej. Odoo 18 con el módulo OCA auth_oidc) se registran con id_token_alg: "RS256" (el id_token_signed_response_alg de OIDC). Solo para esos clientes, el IdP verifica el id_token ES256 genuino y lo re-firma con su propia clave RSA, preservando todos los claims. El JWKS publica la clave pública RSA junto a la clave EC, el discovery anuncia ambos algoritmos, y el token de acceso nunca se re-firma. Cualquier fallo de re-firma vuelve al original ES256.
Los tokens OAuth transportan solo claims de identidad (sub, email, perfil). No hay claim de permiso ni de rol. El hook de token de acceso solo reescribe iss/aud y verifica que el usuario esté habilitado para el cliente.
El catálogo de clientes, la habilitación usuario × plataforma y la auditoría viven en tablas de gobernanza de B Brands gestionadas desde la consola de administración de identidad. El motor solo conserva lo que necesita para acuñar tokens.
Habilitar un acceso dispara un webhook que crea/actualiza el usuario en la plataforma de destino antes de su primer login. El payload transporta identidad y estado, nunca roles. La desprovisión es simétrica al suspender/revocar.
Tokens de acceso OAuth de vida corta + un JWT de exchange de vida corta + una blacklist verificada en cada validación. El JWT de exchange nunca debe heredar el TTL largo del login tradicional.
Un cliente que necesita llamar a las APIs de B Brands en nombre del usuario intercambia su token OIDC por un JWT interno de B Brands y lo usa como Bearer. El autorizador interno existente lo valida. Esto reutiliza el RBAC interno y el Resource Server sin cambios. Consulta Intercambio de tokens.

4. La pieza central: el hook de token de acceso

El hook no inyecta permisos. Su trabajo es reescribir iss/aud, validar que el usuario esté habilitado para el cliente y adjuntar la dimensión de cuenta de negocio (https://bbrands.io/profile). Si el usuario no está habilitado, no se emite ningún token (denegación por defecto seguro).
La lógica de resolución de identidad se describe en Modelo de datos. Modo de fallo: si el hook falla, no se emite ningún token — la seguridad gana.

5. Aislamiento por cliente

Aunque cada cliente comparte una instancia del motor, el aislamiento se garantiza mediante:
  1. El claim https://bbrands.io/client_handle que identifica al cliente solicitante en cada token de acceso OAuth (aud es la audiencia fija bbrands-idp).
  2. La validación de client_handle por cada cliente (un token para crm-prod no es válido en un cliente ERP).
  3. La habilitación mediante el registro de acceso usuario × plataforma, verificada en la emisión, el exchange y el refresh.
  4. El registro de auditoría filtrado por client_handle.

6. Modelo de despliegue