> ## 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.

# Data Model & Lifecycle

> The identity entities, the user × platform enablement model, the provisioning lifecycle and the kill switch.

The identity and enablement layer is a small set of governance entities owned by
the IdP. This page describes them **conceptually**; the concrete storage schema
is an internal implementation detail.

<Note>
  The IdP only authenticates. There is **no** permission catalogue, no per-client
  roles and no authorization tables. Fine-grained authorization lives in each
  client; the B Brands APIs use their existing internal RBAC.
</Note>

## 1. Logical model

```mermaid theme={null}
erDiagram
    User ||--o{ PlatformAccess : "enabled for"
    OAuthClient ||--o{ PlatformAccess : "grants"
    OAuthClient ||--o{ AuthorizationLog : "consent"
    OAuthClient ||--o{ IssuanceLog : "issuance"
    OAuthClient ||--o{ ProvisioningEvent : "webhook queue"
    OAuthClient ||--o{ AdminAudit : "admin changes"
    User ||--o{ TokenBlacklist : "revoked tokens"

    OAuthClient {
        id id PK
        string client_handle UK
        string display_name
        enum platform_type
        enum trust_level
        enum status
        enum id_token_alg
        list redirect_uris
        int access_token_ttl_seconds
    }
    PlatformAccess {
        id id PK
        id user FK
        id oauth_client FK
        enum status
        datetime expires_at
        id granted_by FK
    }
```

Common conventions on every entity: a UUID primary key, standard tracking fields
(created/updated/deleted timestamps and soft-delete flags), restricted referential
integrity, and row-level access controls.

## 2. Entities

| Entity                 | Purpose                                                            |
| ---------------------- | ------------------------------------------------------------------ |
| OAuth client registry  | OAuth client / platform catalogue + governance metadata            |
| User × platform access | User × client enablement (status, **no role**). The central entity |
| Authorization log      | Consent / authorization decisions log                              |
| Token issuance log     | Token issuance log (OAuth + internal exchange)                     |
| Token blacklist        | Immediate revocation by `jti`                                      |
| Provisioning queue     | Webhook provisioning events towards the client                     |
| Admin audit            | Admin change audit                                                 |

### OAuth client registry

Holds one record per registered client, with a stable `client_handle` (e.g.
`erp-dev`), a human-visible display name, `platform_type` (`agent`, `analytics`,
`crm`, `erp`, `internal_tool`, `partner`), `trust_level` (`first_party`,
`partner`, `public`), `status` (`active`, `suspended`, `revoked`), `id_token_alg`
(`ES256` default, `RS256` — the OIDC `id_token_signed_response_alg`: with
`RS256`, the IdP re-signs the client's `id_token` with its RSA key so libraries
that only validate RS256, like Odoo's OCA `auth_oidc` module, work unmodified),
the registered redirect URIs (exact match), a per-client access-token TTL, and
the provisioning webhook target plus its signing secret. The webhook secret is
never returned after creation.

### User × platform access

The central enablement entity. **No role, no permissions.** A user can have at
most one active enablement per client. The access-token hook and the exchange
both check `status = 'active'` and that the enablement has not expired. It tracks
who granted access, optional expiry, and revocation data (when, by whom, reason).

### Logs & audit

<AccordionGroup>
  <Accordion title="Authorization log">
    Consent decisions (`approve`/`deny`), the requested scopes, a correlation id,
    request metadata (IP, user agent) and references to the client and user.
  </Accordion>

  <Accordion title="Token issuance log">
    Grant type (`authorization_code` / `refresh_token` / `token_exchange`), token
    type (`oidc_identity` / `internal_exchange`), `jti`, expiry and a correlation
    id. Append-only, best-effort.
  </Accordion>

  <Accordion title="Token blacklist">
    The revoked token `jti`, token type, reason (e.g. `oauth_revocation`), expiry
    (for cleanup) and who revoked it.
  </Accordion>

  <Accordion title="Provisioning queue">
    Event type (`user_provisioned` / `user_deprovisioned` / `user_suspended` /
    `user_reactivated`), delivery status (`pending` / `in_flight` / `delivered` /
    `failed` / `dead_letter`), the payload, attempt count, next retry time and the
    last error.
  </Accordion>

  <Accordion title="Admin audit">
    The action (client created/updated/suspended/revoked, secret rotated, access
    granted/suspended/revoked/reactivated, …), the actor, the target client/user,
    a correlation id and request metadata.
  </Accordion>
</AccordionGroup>

## 3. Identity resolution & revocation

A single resolution routine is called by the access-token hook. It validates that
the client is active and the user is enabled, and returns the identity payload
(or a structured "not enabled" reason instead of raising, so the hook can answer
a clean `access_denied`). The payload also carries the business-account
dimension: the account bound to the user through the `primary_user` role and
that account's external system id (`null` when unmapped). A typical resolved
payload:

```json theme={null}
{
  "enabled": true,
  "reason": "ok",
  "client_handle": "erp-dev",
  "platform_type": "erp",
  "trust_level": "first_party",
  "access_status": "active",
  "account": "uuid-of-account",
  "account_external_id": "ODOO-84213"
}
```

Two revocation routines back the kill switch:

* **Revoke a single enablement** for a user on one client.
* **Kill switch**: revoke **all** of a user's active/suspended enablements across
  every client at once. Each transition fires a provisioning event.

## 4. Provisioning lifecycle

A status change on the user × platform access entity is the **single** source of
provisioning events:

| Transition                            | Event                |
| ------------------------------------- | -------------------- |
| New enablement with `status = active` | `user_provisioned`   |
| `* → active` (reactivation)           | `user_reactivated`   |
| `* → suspended`                       | `user_suspended`     |
| `* → revoked`                         | `user_deprovisioned` |

```mermaid theme={null}
stateDiagram-v2
    [*] --> pending: event enqueued
    pending --> in_flight: worker picks up
    in_flight --> delivered: 2xx
    in_flight --> failed: 4xx (no retry)
    in_flight --> pending: 5xx / timeout (backoff)
    pending --> dead_letter: max attempts reached
    failed --> [*]
    delivered --> [*]
    dead_letter --> pending: manual retry
```

## 5. Maintenance

The schema is evolved through versioned migrations. Periodic cleanup jobs run on
a schedule: expired blacklist entries (daily), delivered events older than 30 days
(weekly), and issuance/authorization logs older than 12 months archived to cold
storage (monthly).
