> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bbrands.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Seguridad y gobernanza

> Modelo de tokens, gestión de secretos, modelo de amenazas, niveles de revocación, requisitos de auditoría y defensa en profundidad.

Referencia para las revisiones de seguridad y las conversaciones con el equipo de Seguridad. Como
siempre: **el IdP autentica, no autoriza aplicaciones**.

## 1. Modelo de tokens

| Token                   | Formato         | TTL típico            | Emitido por          | Validado por                    |
| ----------------------- | --------------- | --------------------- | -------------------- | ------------------------------- |
| Código de autorización  | Opaco           | 5–10 min              | Motor OAuth          | Motor (endpoint de token)       |
| Token de acceso OAuth   | JWT ES256       | 30 min                | Motor + hook         | App cliente vía JWKS            |
| Refresh token           | Opaco, rotativo | 30 días               | Motor OAuth          | Motor OAuth                     |
| ID token (OIDC)         | JWT ES256       | 30 min                | Motor + hook         | App cliente                     |
| JWT interno de B Brands | JWT HS256       | Corto (900s exchange) | Endpoint de exchange | Autorizador interno de B Brands |

**Distinción clave**: los tokens ES256 demuestran **identidad** y son consumidos por la
**app cliente** — nunca llegan a las APIs de B Brands. El token HS256 es consumido
por las APIs de B Brands vía el autorizador interno existente.

### Claims canónicos del token de acceso OAuth (identidad + dimensión de cuenta)

```json theme={null}
{
  "iss": "https://auth.bbrands.io",
  "sub": "<user_id uuid>",
  "aud": "bbrands-idp",
  "exp": 1734268800,
  "iat": 1734267000,
  "jti": "<uuid>",
  "email": "user@example.com",
  "client_id": "<client uuid>",
  "https://bbrands.io/client_handle": "<client_handle>",
  "https://bbrands.io/platform_type": "<platform_type>",
  "https://bbrands.io/profile": {
    "account": "<account.document_id | null>",
    "odoo": "<account.external_id | null>",
    "user": "<user_id uuid>"
  }
}
```

**Reglas inviolables**: `iss` es siempre la URL `auth.*` por entorno; `aud` es
siempre la audiencia fija del IdP `bbrands-idp`; el cliente solicitante se transporta en
`https://bbrands.io/client_handle`; `jti` está siempre presente e impulsa la
blacklist; **no** hay claim de permisos ni de rol. El claim
`https://bbrands.io/profile` lleva solo contexto de tenancy: `account` es la
cuenta vinculada al usuario estrictamente mediante el rol `primary_user` (un
usuario primario por cuenta), `odoo` es el id del sistema externo de esa cuenta
(`null` hasta que la API de migración la mapee) y `user` es igual a `sub`. El
endpoint UserInfo devuelve el mismo objeto.

### Validación del lado del cliente

```typescript theme={null}
import { jwtVerify, createRemoteJWKSet } from "jose";

const JWKS = createRemoteJWKSet(
  new URL("https://auth.bbrands.io/.well-known/jwks.json"),
  { cooldownDuration: 30_000, cacheMaxAge: 3_600_000 },
);

export async function validateAccessToken(token: string, myHandle: string) {
  const { payload } = await jwtVerify(token, JWKS, {
    issuer: "https://auth.bbrands.io",
    audience: "bbrands-idp",
    algorithms: ["ES256"],
  });
  if (payload["https://bbrands.io/client_handle"] !== myHandle) {
    throw new Error("Token was issued for another client");
  }
  return payload; // identity + account profile, never permissions
}
```

## 2. Dónde vive la autorización

| Escenario                                   | Quién autoriza | Mecanismo                           |
| ------------------------------------------- | -------------- | ----------------------------------- |
| El usuario actúa dentro de una app cliente  | La app cliente | Su propio modelo de permisos/grupos |
| La app cliente consume las APIs de B Brands | B Brands       | RBAC interno existente              |
| Proceso M2M (sin usuario) consume las APIs  | B Brands       | API key + permisos por clave        |

<Warning>
  El JWT de exchange otorga al cliente los permisos **completos** del usuario en B Brands
  (sin least-privilege por cliente). Aceptable para clientes first-party confiables;
  introduce la acotación en el exchange si necesitas reducirlos.
</Warning>

## 3. Gestión de secretos

| Secreto                             | Generado                  | Almacenado en B Brands | Almacenado en el cliente   | Rotación                    |
| ----------------------------------- | ------------------------- | ---------------------- | -------------------------- | --------------------------- |
| `client_secret` OAuth               | B Brands en el onboarding | hasheado en el motor   | secret manager del cliente | 90 días                     |
| Secreto del webhook de provisioning | B Brands en el onboarding | para firmar            | para verificar             | 180 días                    |
| Secreto de firma interno (HS256)    | B Brands                  | env del servidor       | n/a                        | Ante sospecha de compromiso |
| Clave de servicio del motor         | Motor                     | env del servidor       | n/a                        | 365 días                    |
| Clave de firma ES256                | Motor                     | KMS administrado       | n/a                        | Anual, con solapamiento     |
| API key (M2M)                       | B Brands                  | hasheada en reposo     | secret manager del cliente | Política existente          |

Reglas de almacenamiento: nunca en el repositorio, nunca en los logs (enmascara los headers
`Authorization`), nunca en los payloads de webhook, cifrado en reposo, acceso restringido a los
administradores del IdP.

### Rotación de `client_secret`

<Steps>
  <Step title="El administrador hace clic en Rotar">B Brands genera un nuevo secreto.</Step>
  <Step title="Período de gracia">El antiguo y el nuevo se aceptan simultáneamente durante una ventana de gracia configurable (por defecto 24h).</Step>
  <Step title="El cliente actualiza su config">En cualquier momento durante la ventana de gracia.</Step>
  <Step title="Termina la gracia">El secreto antiguo deja de aceptarse; la auditoría registra la rotación.</Step>
</Steps>

### Rotación de la clave de firma ES256

El motor soporta rotación basada en `kid`. Genera la nueva clave, mantén ambas activas,
espera el TTL máximo del token de acceso (30 min) + buffer (1h), retira la clave antigua, luego
elimínala tras la retención. El endpoint JWKS debe exponer ambas durante la ventana.

## 4. Modelo de amenazas (mini-STRIDE)

| #   | Amenaza                                         | Mitigación                                                  |
| --- | ----------------------------------------------- | ----------------------------------------------------------- |
| T1  | Robo de `client_secret`                         | Rotación, alertas de uso anómalo, allowlist de IP opcional  |
| T2  | Intercepción del código de autorización         | TLS + PKCE + `state`                                        |
| T3  | Replay de token                                 | `jti` + blacklist + TTL corto                               |
| T4  | Phishing de la pantalla de consentimiento       | UI clara, sin autoaprobación sin el `trust_level` adecuado  |
| T5  | JWT de exchange con TTL largo                   | TTL corto obligatorio + blacklist                           |
| T6  | Compromiso del usuario                          | MFA en B Brands, alertas de login anómalo, kill switch      |
| T7  | Robo de API key (M2M)                           | Least privilege, rotación, auditoría                        |
| T8  | Replay de webhook                               | `event_id` idempotente                                      |
| T9  | Webhook falsificado                             | Firma HMAC + allowlist de IP                                |
| T10 | Compromiso del hook                             | Revisión de código, deploy desde main, auditoría de cambios |
| T11 | Token del cliente A presentado al cliente B     | Cada cliente valida `https://bbrands.io/client_handle`      |
| T12 | Ataque de timing en la verificación del secreto | Comparación de tiempo constante                             |
| T13 | Race de revocación                              | Blacklist verificada en cada validación                     |

## 5. Niveles de revocación

| Nivel                              | Alcance                                         | Efecto inmediato                                       | Cómo                                |
| ---------------------------------- | ----------------------------------------------- | ------------------------------------------------------ | ----------------------------------- |
| L1: Un solo token                  | Un token de acceso / exchange                   | Sí (blacklist)                                         | `POST /oauth/revoke`                |
| L2: Sesión de usuario              | Todos los refresh tokens del usuario            | El próximo refresh falla                               | Engine admin API                    |
| L3: Usuario × plataforma           | Tokens de ese cliente/usuario                   | El próximo refresh/exchange falla (≤ TTL)              | `DELETE /idp/access/:user/:client`  |
| L4: Cliente completo               | Todos los tokens de ese cliente                 | El próximo refresh falla; los tokens vivos duran ≤ TTL | `POST /idp/clients/:handle/suspend` |
| L5: Usuario completo (kill switch) | Todas las habilitaciones, todas las plataformas | Inmediato si se combina con la blacklist               | Rutina de kill switch               |

## 6. Requisitos de auditoría

* **Inmutable**: append-only, sin UPDATE/DELETE.
* **Trazable**: `actor`, `ip`, `user_agent`, `correlation_id`.
* **Retención**: 12 meses en línea, más tiempo en almacenamiento en frío.
* **Exportable**: `/idp/audit/users/:id/export` para GDPR/compliance.
* **Nunca registrado**: tokens completos (solo el prefijo del `jti`), secretos en texto plano, contraseñas.

## 7. Defensa en profundidad

| Capa           | Control                                                                |
| -------------- | ---------------------------------------------------------------------- |
| Red            | TLS, HSTS, CORS estricto, WAF                                          |
| Cliente OAuth  | PKCE, `state`, redirect URI exacta, validar `iss` + `client_handle`    |
| Endpoint OAuth | Rate limit, validación estricta, auditoría                             |
| Hook           | Verificación de habilitación, denegación por defecto seguro ante error |
| Exchange       | Verificar subject token + habilitación + TTL corto + blacklist         |
| Token interno  | HS256, RBAC interno, blacklist                                         |
| Operacional    | MFA de administradores, registro de auditoría, alertas, runbooks       |
