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

# Spec de módulo — identity (schema identity)

> Es dueño del realm organizacional y del plano de control de acceso de la plataforma: quiénes son los equipos de nuestros clientes, qué pueden hacer, y cómo las máquinas prueban que pueden hacerlo. No depende de nada (ADR-022).

> Traducción. Autoritativo: [`../../../modules/identity/spec.md`](/modules/identity/spec).

El **realm de member se queda en `core`** (ADR-010, FS-CORE-0009). Un member es un contacto
autenticado; un usuario no.

## Entidades e invariantes

| Tabla                 | Invariantes clave                                                                                                                                                                        |
| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| organizations         | una por tenant; `slug` único; configuración de facturación y locale; nunca se borra en duro                                                                                              |
| users                 | `organization_id` NOT NULL; email único **por (organización, email)** — nunca global (`standards/data.md` §2b); la misma dirección en dos organizaciones son dos usuarios independientes |
| memberships           | (usuario, organización) único; lleva los roles; desactivar nunca borra historia                                                                                                          |
| invitations           | token de un solo uso con expiración; las aceptadas se vuelven membresías y se conservan como evidencia                                                                                   |
| permissions           | el registro: `{module}.{resource}.{action}`; generado desde las declaraciones de ruta, sembrado, nunca creado por el tenant                                                              |
| roles                 | roles de sistema sembrados por organización + roles definidos por el tenant; un rol es un conjunto de permisos                                                                           |
| role\_assignments     | sujeto polimórfico: usuario **o** máquina (DEC-D5); a lo más una asignación abierta por (sujeto, rol)                                                                                    |
| auth\_accounts        | vínculo de proveedor de Better Auth: contraseña, magic link, passkey, proveedor oauth                                                                                                    |
| mfa\_factors          | TOTP y códigos de recuperación; `verified_at`; los códigos son de un solo uso y hasheados                                                                                                |
| sessions              | dispositivo, IP, user agent, `expires_at`, `revoked_at`; revocables individual o masivamente                                                                                             |
| api\_keys             | secreto hasheado, prefijo en claro para identificación, roles asociados, `last_used_at`, rotable                                                                                         |
| oauth\_clients        | integraciones de terceros: client id, secreto hasheado, redirect URIs, scopes permitidos, **realm objetivo** `user`\|`member` (nunca ambos)                                              |
| oauth\_authorizations | grants emitidos con los scopes que el usuario consintió; revocables por el usuario                                                                                                       |
| impersonations        | operador, objetivo, razón, ventana de tiempo, `ended_at`; append-only, visible para el tenant                                                                                            |

## Eventos emitidos

`identity.user.invited|joined|deactivated` · `identity.role.assigned|revoked` ·
`identity.api_key.created|rotated|revoked` · `identity.oauth.authorized|revoked` ·
`identity.impersonation.started|ended` · `identity.mfa.enabled|disabled`

## Endpoints

* Auth: `POST /v1/identity/auth/{sign-in,sign-up,sign-out,magic-link,passkey}` — público, con rate limit
* MFA: `POST /v1/identity/mfa/{enroll,verify,disable}` — `identity.mfa.*`
* Usuarios y membresías: CRUD `/v1/identity/users`, `/v1/identity/memberships`
* Roles: CRUD `/v1/identity/roles`, `POST /v1/identity/role-assignments` — `identity.roles.*`
* Credenciales de máquina: CRUD `/v1/identity/api-keys`, `/v1/identity/oauth-clients`
* OAuth2 provider: `GET /oauth/authorize`, `POST /oauth/token`, `POST /oauth/revoke`, `GET /oauth/userinfo`
* Impersonación: `POST /v1/identity/impersonations` — solo Ops, `identity.impersonation.start`

## OAuth corre en ambas direcciones

Softcrum es **provider** OAuth2/OIDC (FS-IDN-0007) para que una herramienta socia actúe por cuenta
del tenant y el sitio del tenant ofrezca "Sign in with Softcrum", **y** **client** OAuth2/OIDC
(FS-IDN-0008) para que el personal del tenant entre por su propio proveedor de identidad. Ninguna es
el default; ambas son configuración, y un tenant puede usar las dos a la vez.

## No negociables

Toda restricción de unicidad lleva el tenant. Dos realms, nunca superpuestos. Cada endpoint de la
plataforma declara exactamente un permiso de este registro. Las máquinas llevan roles igual que las
personas. Ningún módulo lee estas tablas directamente — la capa de API resuelve el contexto de
autorización y lo pasa hacia abajo.
