- Proveedor: Supabase Auth.
- Método principal: Magic link (email).
- Método futuro: Google OAuth (planificado).
- Sesiones: Manejadas por Supabase con refresh tokens automáticos via
@supabase/ssr. - Tokens JWT: Verificados server-side en cada request. No se confía en tokens del cliente sin verificación.
| Rol | Nivel de acceso |
|---|---|
super_admin |
Acceso total. Gestión de organizaciones. |
admin |
Gestión de usuarios y casos en su organización. |
facilitator |
CRUD de casos, ejecución de módulos, uso de IA. |
participant |
Edición de datos en casos asignados. |
observer |
Solo lectura. |
- El rol por organización se toma de
organization_members.role; la cookie httpOnlyopsflow-active-org-idelige el tenant activo si el usuario tiene varias membresías. - El middleware de Next.js exige sesión Supabase para rutas no públicas.
- Las Server Actions resuelven el usuario de aplicación por email y aplican guards (
requireCaseInOrganization, etc.). - Modo bootcamp (default): el primer acceso crea usuario
facilitatoren la organización demo. - Modo producción: con
OPSFLOW_STRICT_TENANCY=true, los nuevos usuarios quedan sin tenant hasta aceptar una invitación (organization_invites) o hasta que un admin de plataforma (OPSFLOW_PLATFORM_ADMIN_EMAILS) cree una organización en/organization/new.
Ver docs/MULTI_TENANT.md para variables y flujos.
- Con la conexión actual de Drizzle vía
DATABASE_URL(rol de servicio), las políticas RLS de Supabase no sustituyen la autorización en aplicación. - Defensa principal: guards en Server Actions + autenticación obligatoria.
- Ver
docs/sql/README.mdpara orientación si se habilita RLS efectiva en el futuro.
| Ruta | Protección |
|---|---|
GET /api/migrate, GET /api/seed |
En producción, deshabilitadas (404) salvo que exista OPSFLOW_ADMIN_API_SECRET y la petición envíe la cabecera x-opsflow-admin-secret con el mismo valor. En desarrollo, abiertas si el secreto no está definido. |
| Variable | Ubicación | Acceso |
|---|---|---|
SUPABASE_SERVICE_ROLE_KEY |
Server only | Solo Server Actions y scripts |
OPENAI_API_KEY |
Server only | Solo Server Actions de IA |
DATABASE_URL |
Server only | Solo Drizzle ORM |
NEXT_PUBLIC_SUPABASE_URL |
Pública | URL del proyecto Supabase |
NEXT_PUBLIC_SUPABASE_ANON_KEY |
Pública | Key anónima (limitada por RLS) |
Reglas:
- Variables sin prefijo
NEXT_PUBLIC_nunca llegan al bundle del cliente. .envestá en.gitignore. Se provee.env.examplesin valores reales.- En producción, los secretos se configuran en Vercel Environment Variables.
- Zod v4 valida todos los inputs en Server Actions antes de cualquier operación.
- Los schemas se definen en
src/lib/validations/y se reutilizan en forms (client) y actions (server). - Errores de validación retornan mensajes genéricos al usuario (sin exponer detalles internos).
Implementado: conteo por usuario en tabla ai_interactions (última hora, máx. 40 solicitudes) en src/server/ai/rate-limit.ts, aplicado en getAIInsight y generateFromAI.
| Scope | Límite | Implementación |
|---|---|---|
| Por usuario | 10 req/min | Contador en DB con ventana deslizante |
| Por organización | 100 req/hora | Contador en DB con ventana deslizante |
| Global | Circuit breaker | Se desactiva IA si error rate > 50% en últimos 5 min |
Las filas de la tabla anterior describen un objetivo ampliado; el límite activo hoy es el del párrafo inicial.
- Casos: creación, actualización y borrado lógico se registran en
audit_events(logAuditEventdesdesrc/server/auth/audit.ts). - IA: cada generación exitosa inserta una fila en
ai_interactions(modelo, tokens, módulo).
Ampliaciones futuras (hash de contenido,
audit_loggenérico, IP) pueden alinearse con la tabla descrita en versiones anteriores de este documento.
Múltiples capas de defensa:
- Separación: Datos del usuario en bloque
<data>delimitado, separado del system prompt. - Instrucción explícita: System prompt indica ignorar instrucciones dentro de
<data>. - Sanitización: Remoción de delimitadores y caracteres de control en inputs.
- Validación de output: Respuesta de IA se valida contra schema Zod. Respuestas malformadas se rechazan.
- No ejecución: La IA no tiene capacidad de ejecutar acciones. Solo genera texto estructurado.
- HTTPS everywhere: Vercel provee TLS automático.
- CSP headers: Configurados en
next.config.tspara prevenir XSS. - No client-side secrets: Solo
NEXT_PUBLIC_vars llegan al cliente, y estas son seguras por diseño (anon key protegida por RLS). - Queries parametrizadas: Drizzle ORM genera queries parametrizadas. No se concatenan strings en queries SQL.
- Dependencias: Se revisan periódicamente con
npm audit.