---
url: /reference/IDENTITY_ACCESS_AUDIT.md
---
# Auditoría de Identidad, Organización y Acceso — revisión 9

> Documento migrado desde `franmc01/ninaku-core-api`.

> Estado posterior: [la revisión 10](../history/REFINEMENT_10.md) agrega la protección H05 durante la vida de organizaciones provisionadas, transferencia, bloqueo/recuperación privilegiados, cierre terminal de soporte, ciclo de PIN y revocación de sesiones por credenciales comprometidas. Este documento conserva el diagnóstico y la evidencia histórica de la revisión 9; sus pendientes deben leerse junto con esa actualización.

## Alcance y evidencia

Esta revisión endurece contratos de persistencia del baseline SQL. El repositorio todavía no implementa un servidor de autenticación: insertar `verified_at` no demuestra que un correo recibió un OTP, ni insertar una sesión demuestra que se verificó una contraseña.

Base revisada: `1764cf6bfa8227cc3e2500490a34430f3695389c`, PR #1. La reproducción se publicó primero en `29b2e9fd5987a80d2029704b2bc56f8516e0d74e`: ejecución roja de PostgreSQL (histórico, no migrado). Las regresiones anteriores pasaron; el nuevo script reportó 27 fallos de 35 comprobaciones. **25 corresponden a violaciones de estado reproducidas; dos fueron expectativas incorrectas del test**: TN01 recibía una validación de negocio antes de RLS y SC01 chocaba con unicidad antes de probar el traslado de alcance. Ambas pruebas fueron corregidas. No son 27 vulnerabilidades de una API publicada.

**PASS PostgreSQL 18.6** del código `ad72ce751c8e14db335feec9286a34595da3e9a0`; GitHub ejecutó su merge de prueba `dbb7c307f41c00d3eaf462b742451c38972de541` con main. Ejecución y logs (histórico, no migrado). Se instalaron 41 SQL, 808 tablas, 89 filas iniciales y 722 tablas tenant con RLS. Pasaron las regresiones anteriores y las **52 comprobaciones nuevas**, sin fallos. H05 se reproduce por separado y continúa abierto. El commit posterior únicamente registra evidencia documental.

| Grupo | Comprobaciones aprobadas |
|---|---:|
| Alta y rollback | 5 |
| Principal e identificador | 7 |
| Desafíos, intentos y consumo concurrente | 9 |
| Credenciales, TOTP y recuperación | 3 |
| Sesiones y bloqueo concurrente | 7 |
| Invitaciones | 7 |
| Tenant y contexto de conexión | 5 |
| Alcances y concurrencia | 5 |
| Vigencia de soporte | 4 |
| **Total** | **52** |

El script genera `test-results/identity-adversarial.json` y su log, disponibles en el artifact de la ejecución. Las pruebas concurrentes de sesiones y alcances comprueban contención con `pg_blocking_pids`; el consumo de desafíos comprueba un único ganador. No se agregaron tablas ni un framework de estados. Es un baseline de instalación limpia; bases existentes necesitan una migración y revisión de datos históricos antes de aplicar estas restricciones.

## Una fuente de verdad por entidad

No se agrega un motor genérico ni un segundo estado que contradiga las columnas existentes.

| Entidad | Fuente de estado | Transiciones y sentido |
|---|---|---|
| Principal global | `identities.status` | `active`, `blocked`, `disabled`, `archived`. Archivado es terminal. Activo significa disponible; no significa correo verificado, sesión autenticada ni acceso a un tenant. El tipo humano/servicio no cambia. |
| Identificador de acceso | `login_identifiers.verified_at/revoked_at` | Pendiente → verificado; pendiente o verificado → revocado. Revocado prevalece. Cambiar correo/teléfono requiere otro registro y verificación; no se transfiere la verificación anterior. |
| Método de autenticación | `authentication_methods.status/revoked_at` | Activo ↔ deshabilitado mientras no esté revocado. Comprometido o revocado requiere reemplazo. El cambio a comprometido registra revocación. |
| Desafío de autenticación | `consumed_at/revoked_at/expires_at/attempt_count/max_attempts` | Nuevo pendiente; consumido, revocado, vencido o agotado no es utilizable. Consumo/revocación son terminales; los intentos no retroceden ni se modifica sujeto, propósito, secreto o plazo. |
| TOTP / código de recuperación | Verificación y revocación / uso y revocación | Un secreto TOTP nuevo requiere otro enrolamiento; un código usado no vuelve a estar disponible. La verificación criptográfica pertenece al servicio Auth. |
| Sesión | `sessions.revoked_at/expires_at` | Emitir exige principal activo. Revocada o vencida no admite rotación. Dueño y plazo no cambian; renovar duración requiere otra sesión. Bloquear/deshabilitar/archivar revoca sesiones existentes; reactivar el principal no las resucita. |
| Invitación | `accepted_at/revoked_at/expires_at` | Nueva pendiente → aceptada o revocada; el vencimiento se deriva del reloj. Aceptar exige humano activo con el identificador invitado verificado y vigente. Destinatario, prueba y plazo no cambian. Se congela su configuración al aceptar, revocar o vencer. |
| Acceso a organización | `memberships.status` | Activo ↔ suspendido; activo/suspendido → terminado. Terminado es terminal; un reingreso crea otra membresía. Suspender aquí afecta a esta organización; bloquear el principal afecta a todas. |
| Soporte con suplantación | `started_at/expires_at/ended_at` | Antes del inicio no autoriza; después del vencimiento tampoco. La comprobación usa el reloj de cada sentencia, incluso en una transacción larga. Se puede registrar el cierre después del vencimiento. |

El acceso efectivo debe conjugar principal activo, organización activa, membresía activa, permiso y alcance vigentes, capacidad contratada y admisión del comando. RLS aísla tenants; **no sustituye verificar que el actor tiene permiso para administrar roles**. El contexto SQL lo establece un backend confiable después de validar la sesión; nunca debe provenir libremente del cliente.

La cuenta de acceso (Identity) es distinta del cliente persona/empresa (Party) y de su cuenta corriente/deuda. Un cliente puede acumular ventas sin tener login. La organización es el tenant; puede contener varias entidades legales/RUC, sucursales y cajas. Los permisos delimitan dónde opera cada persona; no fusionan deudas, saldos ni titularidad legal entre RUC. El catálogo y el surtido siguen siendo contratos separados de identidad.

## Flujos que debe implementar el primer módulo de aplicación

**Primer módulo: Identidad y acceso, incluyendo alta y pertenencia a organizaciones.** Organización constituye su contexto. No iniciar Ventas mientras la admisión de comandos todavía permita confundir al actor, al tenant o su alcance.

1. **Alta:** normalizar identificador, aplicar controles de abuso, generar hash de contraseña en el servicio Auth; persistir principal, identificador pendiente, método y desafío en una transacción. Enviar la verificación después de confirmar, con una entrega reintentable. Un duplicado debe revertir el alta completa. No crear una membresía por el hecho de registrarse.
2. **Verificación:** comprobar secreto, propósito, destinatario, vencimiento e intentos; consumir condicionalmente la prueba y verificar el identificador en la misma transacción. Dos consumidores no deben ganar. Una entrega repetida no debe repetir efectos. Una prueba fallida no debe perder el incremento de intentos por un rollback mal diseñado.
3. **Ingreso y renovación:** comprobar credenciales y política de verificación/MFA; emitir sesión solo después. Implementar hash de refresh, rotación atómica, detección de reutilización, cierre de sesión y revocación. El SQL solo conserva el hash actual: aún no existe una familia de refresh tokens con detección de replay.
4. **Crear organización o aceptar invitación:** resolver identidad verificada, cupos, rol autorizado y alcances; crear organización/membresía/asignaciones con idempotencia y transacción. La aceptación y la membresía deben confirmarse juntas. Los triggers validan la aceptación persistida, pero no crean automáticamente la membresía ni prueban el token recibido.
5. **Administración:** invitar/revocar/reenviar, suspender/reactivar/terminar membresía, cambiar roles y alcances, transferir administración y auditar actor/objetivo/motivo. Configurar alcances usa READ COMMITTED y bloqueos de padres; una reasignación exige otorgar/revocar explícitamente.
6. **Recuperación y baja:** recuperar contraseña sin enumerar cuentas, reemplazar credenciales comprometidas, restablecer MFA bajo un procedimiento verificable, bloquear y cerrar sesiones. Política de retención y recuperación de administración pendiente; no implementar baja como borrado indiscriminado del historial.

Pruebas de alta verifican atomicidad y ausencia de derechos automáticos; no prueban correo, contraseña, CAPTCHA, OIDC ni endpoints inexistentes. Alta de servicio y alta humana requieren políticas distintas.

## Correcciones y pruebas adversariales

* Congelar identidad/procedencia, verificación y evidencia terminal; impedir reset de intentos, reutilización de códigos y cambios silenciosos de secreto TOTP.
* Serializar emisión de sesiones y cambios de estado del principal; revocar las sesiones al desactivar y mantenerlas revocadas al reactivar. Probar ambos órdenes concurrentes: emisión antes del bloqueo y bloqueo antes de la emisión.
* Verificar destinatario de invitación y vencimiento real; no permitir insertar una invitación ya aceptada para saltar la transición; preservar configuración aceptada.
* Serializar cambios de dimensiones de alcance por asignación e invitación. Se prueba bloqueo concurrente y rechazo de una segunda dimensión incompatible.
* Impedir trasladar un alcance a otra asignación dejando la original huérfana.
* Probar aislamiento entre tenants, contexto falsificado y limpieza de contexto LOCAL al reutilizar conexión.
* Revalidar el tiempo de soporte en cada sentencia y permitir registrar su cierre tras vencer.
* Mantener la regresión previa de organización, cuotas, catálogo, tres cajas, ventas, inventario, caja y contabilidad.

Los roles adversariales son NOLOGIN, NOSUPERUSER y NOBYPASSRLS. El rol Auth tiene escritura global deliberada para probar invariantes de persistencia del servicio confiable; no representa un usuario final con acceso SQL. La función trigger de invitaciones usa SECURITY DEFINER y search\_path fijo únicamente para consultar/bloquear el principal y su identificador; la escritura de invitación sigue sujeta a RLS. No se conceden a tenants permisos de escritura sobre credenciales globales.

## Offline: contrato necesario, todavía no certificado

El cliente puede conservar una autorización local de duración limitada para operación previamente habilitada. No puede descubrir un bloqueo remoto mientras está desconectado. Debe existir un plazo máximo y una política explícita para ese intervalo.

Alta, verificación, recuperación, aceptación de invitaciones y otorgamiento de permisos requieren validación del servidor; un cliente offline no debe convertirse en autoridad de identidad. Para operaciones comerciales encoladas, conservar actor, dispositivo, organización, entidad legal, alcance, versión de autorización e idempotency key. Al reconectar, revalidar y separar rechazo/conflicto de aceptación; no borrar movimientos de caja ni producir ventas duplicadas. Hora declarada por dispositivo no prueba que una operación ocurrió antes de una revocación.

Este baseline y esta auditoría no certifican autorización offline completa ni revocación de dispositivo durante desconexión.

## Brechas abiertas y límites

* **H05 — Último administrador:** se reproduce que borrar su única asignación deja la organización sin administrador. El script imprime `NINAKU_KNOWN_GAP_H05_LAST_ADMINISTRATOR`; no cuenta como capacidad aprobada. Falta contrato de transferencia/recuperación y protección concurrente de todos los caminos que quitan acceso, incluyendo cambios de rol, membresía y principal. Un trigger aislado de DELETE sería insuficiente.
* **H06 — Cambios retroactivos de entidad legal/unidad de negocio:** requieren revisar efecto sobre autorización e historial.
* **H07 — Fecha de autorización:** `effective_permission` aún usa `current_date`; falta explicitar fecha local de operación frente a timezone de sesión. El arreglo del reloj de soporte no resuelve este asunto.
* **H08 — Dispositivo revocado/offline:** falta contrato de admisión y resolución al sincronizar.
* Falta backend de autenticación y autorización por comando, delivery, criptografía, límites de intentos por cuenta/IP/dispositivo, protección contra enumeración, MFA/recovery completo, auditoría de transiciones e idempotencia de endpoints.
* Una credencial comprometida debe detonar política de cierre de sesiones; esta revisión revoca automáticamente por estado del principal, no por cada cambio de credencial.
* La revisión temporal de soporte no endurece todavía todas las ediciones de su autorización: actor, objetivo, plazo y reapertura de un cierre requieren una siguiente batería específica. El ciclo del PIN operativo, las credenciales externas/passkeys y sus reemplazos tampoco está certificado por estas pruebas.
* Las escrituras iniciales verificadas de identificadores/TOTP se reservan a un servicio confiable (p. ej. evidencia de proveedor externo); SQL no verifica esa evidencia. No dar DML global de Auth a clientes.
* Las pruebas son un conjunto definido de invariantes, no una promesa de todas las casuísticas ni una certificación de producción.
