# ADR-004 — Capa de identidad: respondente anónimo vs staff con SSO

- **Estado:** Aceptado (frontera) · Diferido (integración Keycloak)
- **Fecha:** (completar con fecha de commit)

---

## Contexto

El equipo construye una familia de productos Inquality que se autentican contra un **Keycloak**
propio (proyecto en curso de otro equipo). Surge la pregunta de si esa capa de identidad aplica a
este producto. El producto tiene dos audiencias con necesidades opuestas (respondente anónimo vs
staff interno), por lo que la respuesta no es única.

## Decisión

### Frontera dura (no negociable)
- **El flujo del respondente NUNCA se ata a un IdP.** Las encuestas de v1 son anónimas: el
  respondente no se loguea, ni con Keycloak ni con nada. Atarlo a SSO destruiría el anonimato (la
  premisa de v1) y agregaría fricción que baja la tasa de respuesta. Lado respondente = `anon` key +
  RLS + sin identidad. Para siempre, mientras la encuesta sea anónima.
- **El staff interno SÍ puede usar SSO.** El acceso del equipo a dashboards/administración —a través
  de la familia de productos— es donde Keycloak aporta: SSO único, gestión de usuarios centralizada,
  offboarding en un solo lugar (control de seguridad real para dato sensible), una sola fuente de
  verdad de identidad. Y Keycloak es self-hosteable → alinea con la dirección Dokploy/soberanía.

### Integración técnica (cuando Keycloak madure)
Supabase soporta *third-party auth*: confía en JWT de un emisor externo si son firmados
asimétricamente (RS256/ES256), expuestos vía OIDC Discovery URL, con `kid` en el header. Keycloak
cumple. Dos caminos válidos:
1. Registrar Keycloak como emisor OIDC confiable (más libre en self-host).
2. Patrón universal: verificar el JWT de Keycloak en el edge e intercambiarlo por un JWT firmado por
   Supabase. Funciona con cualquier emisor.
RLS lee los claims del token externo con `auth.jwt()` igual que con tokens de Supabase Auth.

### No bloquear v1
Keycloak es un proyecto en curso de otro equipo. **v1 no depende de él.** v1 casi no necesita auth
(encuestas anónimas + dashboard que presenta el staff). Estrategia:
1. Diseñar las policies de RLS para que lean **claims por nombre** (`org_id`, `role`), no helpers
   exclusivos de Supabase Auth, para que el emisor sea intercambiable.
2. v1 corre con auth nativa de Supabase para los pocos usuarios internos (o sin login interno si
   alcanza).
3. Cuando Keycloak esté listo: emite esos claims, se registra como emisor confiable, y RLS sigue
   andando sin reescribir.

## Consecuencias
- (+) Identidad portable: el emisor del token se cambia por config, no por reescritura.
- (+) Encaja con la familia de productos y con la soberanía (Keycloak self-host).
- (−) Disciplina: nunca basar RLS en `user_metadata` (modificable por el usuario); usar claims
  verificados del servidor (Keycloak los da).
- (−) Cuando se integre, alinear los claims de Keycloak (`org_id`, `role`) con lo que esperan las
  policies — tarea de config, no de esquema.

## Relación con otros ADR
Vive sobre el patrón de backend de ADR-002 (Supabase como backend). La regla de keys de `CLAUDE.md`
(rule 9) sigue aplicando: `service_role` nunca al cliente, pase lo que pase con el IdP.