56 lines
3.2 KiB
Markdown
56 lines
3.2 KiB
Markdown
# 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. |