Convierte recordaLexia de una sola familia a multi-familia, con cuentas propias y persistencia de preferencias. Backend: - Tenant Family (email único + contraseña BCrypt + PIN + prefs de cuenta); family_id en child/activity/material_item/reward; aislamiento por familia (acceso cruzado responde 404). - Auth propia (sin Keycloak): registro/login email+contraseña, sesiones de familia persistidas en BD (sobreviven a reinicios), panel de padres tras PIN. - Liquibase 002-multitenant; seeder crea una familia demo. - Tests de aislamiento entre familias, registro/login y gate del panel. Frontend: - Login, registro y pantalla de cuenta; guards (sesion + PIN) e interceptor de sesion global; perfiles scopeados a la familia. Preferencias: - OpenDyslexic persistida por nino (child.dyslexiaFont) y default de cuenta. Decisiones en docs/adr/0004.
43 lines
2.1 KiB
Markdown
43 lines
2.1 KiB
Markdown
# ADR 0004 — Multi-tenant + registro/login propio (email/contraseña)
|
|
|
|
- **Estado:** aceptada
|
|
- **Fecha:** 2026-06-21
|
|
- **Supersede (parcialmente):** ADR 0002/0003 en lo relativo a "auth ligera sin cuentas".
|
|
|
|
## Contexto
|
|
|
|
El contrato original definía la app como **una sola familia**, con auth ligera
|
|
(PIN de padres) y datos globales. El usuario pide convertirla en **multi-familia
|
|
(multi-tenant)** con **registro/login propio** y persistencia de preferencias.
|
|
|
|
## Decisión
|
|
|
|
1. **Tenant = `Family`** (cuenta con email único + contraseña BCrypt + PIN + prefs).
|
|
Las entidades raíz (`child`, `activity`, `material_item`, `reward`) llevan
|
|
`family_id`. El resto cuelga del niño.
|
|
2. **Aislamiento**: toda consulta raíz se filtra por la familia de la sesión; las
|
|
operaciones por id verifican pertenencia y, si no, responden **404** (no 403,
|
|
para no filtrar existencia).
|
|
3. **Sesión de familia ligada al dispositivo**, persistida en BD (`family_session`)
|
|
para sobrevivir a reinicios (clave para el kiosko). Cabecera `X-Auth-Session`.
|
|
Toda la API exige sesión válida; el niño NO se loguea (el adulto deja la sesión
|
|
abierta en la tablet).
|
|
4. **Panel de padres**: además de la sesión, exige **desbloqueo con PIN**
|
|
(`POST /api/parents/unlock`), que concede el rol PARENT durante 30 min.
|
|
5. **Sin Keycloak**: auth propia encapsulada en el paquete `security`
|
|
(`AuthService`, `SessionAuthService`, `SessionAuthFilter`).
|
|
6. **Preferencia OpenDyslexic** pasa a campo por niño (`child.dyslexia_font`) + un
|
|
default de cuenta (`family.default_dyslexia_font`).
|
|
|
|
## Consecuencias
|
|
|
|
- Registro abierto (cualquiera crea una familia). Rate-limiting y verificación de
|
|
email quedan como mejora futura (homelab).
|
|
- La migración del esquema (`002-multitenant.yaml`) añade `family_id` NOT NULL; en
|
|
BD ya poblada habría que hacer backfill (en este proyecto se parte de BD limpia
|
|
con `docker compose down -v`).
|
|
- La sesión de 30 días en el dispositivo es un compromiso UX/seguridad razonable
|
|
para un kiosko doméstico; revocable borrando la fila de `family_session`.
|
|
- Sustituir la auth por un IdP externo (Keycloak) solo afectaría al paquete
|
|
`security`.
|