Some checks are pending
CI Pipeline / changes (push) Waiting to run
CI Pipeline / core (push) Blocked by required conditions
CI Pipeline / trading-backend (push) Blocked by required conditions
CI Pipeline / trading-data-service (push) Blocked by required conditions
CI Pipeline / trading-frontend (push) Blocked by required conditions
CI Pipeline / erp-core (push) Blocked by required conditions
CI Pipeline / erp-mecanicas (push) Blocked by required conditions
CI Pipeline / gamilit-backend (push) Blocked by required conditions
CI Pipeline / gamilit-frontend (push) Blocked by required conditions
Gamilit: - Backend: Teacher services, assignments, gamification, exercise submissions - Frontend: Admin/Teacher/Student portals, module 4-5 mechanics, monitoring - Database: DDL functions, seeds for dev/prod, auth/gamification schemas - Docs: Architecture, features, guides cleanup and reorganization Core/Orchestration: - New workspace directives index - Documentation directive Trading-platform: - Database seeds and inventory updates - Tech leader validation report 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
269 lines
11 KiB
Markdown
269 lines
11 KiB
Markdown
# Arquitectura de Autenticación - GAMILIT
|
|
|
|
**Fecha:** 2025-12-14
|
|
**Versión:** 1.0
|
|
**Estado:** Documentado
|
|
**Auditoría:** AUDIT-DB-001
|
|
|
|
---
|
|
|
|
## 1. Resumen Ejecutivo
|
|
|
|
Este documento describe la arquitectura de autenticación de GAMILIT, aclarando que el proyecto **NO utiliza Supabase** como servicio. El esquema `auth` y sus tablas siguen un **patrón estándar de la industria** para autenticación con PostgreSQL y Row Level Security (RLS).
|
|
|
|
---
|
|
|
|
## 2. Arquitectura de Dos Capas
|
|
|
|
GAMILIT implementa una arquitectura de autenticación de dos capas:
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ CAPA 1: AUTH BASE │
|
|
│ Schema: auth │
|
|
├─────────────────────────────────────────────────────────────┤
|
|
│ auth.users │
|
|
│ ├── id (UUID PK) │
|
|
│ ├── email (unique) │
|
|
│ ├── encrypted_password │
|
|
│ ├── email_confirmed_at │
|
|
│ ├── last_sign_in_at │
|
|
│ ├── raw_user_meta_data (JSONB) │
|
|
│ ├── created_at │
|
|
│ └── updated_at │
|
|
│ │
|
|
│ Propósito: Credenciales de autenticación puras │
|
|
│ Acceso: Solo servicios de autenticación │
|
|
└─────────────────────────────────────────────────────────────┘
|
|
│
|
|
│ FK: profile_id → auth.users.id
|
|
▼
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ CAPA 2: AUTH MANAGEMENT │
|
|
│ Schema: auth_management │
|
|
├─────────────────────────────────────────────────────────────┤
|
|
│ auth_management.profiles │
|
|
│ ├── id (UUID PK, FK → auth.users.id) │
|
|
│ ├── first_name │
|
|
│ ├── last_name │
|
|
│ ├── display_name │
|
|
│ ├── avatar_url │
|
|
│ ├── school_id (FK) │
|
|
│ ├── grade_level │
|
|
│ ├── date_of_birth │
|
|
│ ├── preferences (JSONB) │
|
|
│ └── tenant_id (FK) │
|
|
│ │
|
|
│ Propósito: Datos de perfil de negocio │
|
|
│ Acceso: Toda la aplicación │
|
|
└─────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
### 2.1 Por qué Dos Capas
|
|
|
|
| Aspecto | auth.users | auth_management.profiles |
|
|
|---------|------------|--------------------------|
|
|
| **Datos** | Credenciales (email, password) | Información de negocio (nombre, avatar) |
|
|
| **Seguridad** | Altamente restringido | Accesible con RLS |
|
|
| **Modificación** | Solo sistema de auth | Usuario y administradores |
|
|
| **Referencias** | Ninguna (aislado) | Todas las tablas del sistema |
|
|
|
|
---
|
|
|
|
## 3. Roles RLS (Row Level Security)
|
|
|
|
GAMILIT usa tres roles estándar para RLS:
|
|
|
|
```sql
|
|
-- Definidos en 00-prerequisites.sql
|
|
CREATE ROLE authenticated;
|
|
CREATE ROLE anon;
|
|
CREATE ROLE service_role;
|
|
|
|
-- Descripciones
|
|
COMMENT ON ROLE authenticated IS 'Rol RLS: usuarios autenticados (cualquier rol GAMILIT)';
|
|
COMMENT ON ROLE anon IS 'Rol RLS: visitantes no autenticados';
|
|
COMMENT ON ROLE service_role IS 'Rol RLS: acceso elevado para operaciones del sistema';
|
|
```
|
|
|
|
### 3.1 Mapeo de Roles
|
|
|
|
| Rol RLS | Rol GAMILIT | Permisos |
|
|
|---------|-------------|----------|
|
|
| `authenticated` | student, teacher, admin, super_admin | Lectura/escritura según policies |
|
|
| `anon` | (sin login) | Solo lectura de datos públicos |
|
|
| `service_role` | Backend interno | Bypass de RLS para operaciones del sistema |
|
|
|
|
---
|
|
|
|
## 4. Flujo de Autenticación
|
|
|
|
```
|
|
┌──────────┐ ┌──────────────┐ ┌─────────────┐ ┌──────────────────┐
|
|
│ Cliente │────▶│ POST /login │────▶│ AuthService │────▶│ auth.users │
|
|
└──────────┘ └──────────────┘ └─────────────┘ │ (validar creds) │
|
|
│ └──────────────────┘
|
|
│
|
|
▼
|
|
┌─────────────┐
|
|
│ JWT Token │
|
|
│ + user_id │
|
|
│ + role │
|
|
└─────────────┘
|
|
│
|
|
▼
|
|
┌──────────────────────────────────────────────────────┐
|
|
│ Headers: Authorization: Bearer <token> │
|
|
│ PostgreSQL: SET LOCAL role = 'authenticated'; │
|
|
│ SET LOCAL request.jwt.sub = '<user_id>';│
|
|
└──────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
### 4.1 JWT Claims
|
|
|
|
```typescript
|
|
interface JWTPayload {
|
|
sub: string; // auth.users.id (UUID)
|
|
email: string; // auth.users.email
|
|
role: string; // GAMILIT role (student, teacher, etc.)
|
|
tenant_id?: string; // Multi-tenant support
|
|
iat: number; // Issued at
|
|
exp: number; // Expiration
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 5. Tablas de Autenticación
|
|
|
|
### 5.1 Schema `auth`
|
|
|
|
| Tabla | Descripción | Columnas Clave |
|
|
|-------|-------------|----------------|
|
|
| `users` | Credenciales de usuario | id, email, encrypted_password, email_confirmed_at |
|
|
|
|
### 5.2 Schema `auth_management`
|
|
|
|
| Tabla | Descripción | FK Principal |
|
|
|-------|-------------|--------------|
|
|
| `profiles` | Datos de perfil extendido | id → auth.users.id |
|
|
| `tenants` | Organizaciones multi-tenant | - |
|
|
| `memberships` | Relación usuario-tenant | user_id → profiles.id |
|
|
| `roles` | Definición de roles | - |
|
|
| `user_roles` | Asignación usuario-rol | profile_id → profiles.id |
|
|
| `user_sessions` | Sesiones activas | user_id → profiles.id |
|
|
| `auth_providers` | Proveedores externos (Google, etc.) | user_id → profiles.id |
|
|
| `auth_attempts` | Log de intentos de login | user_id → profiles.id |
|
|
| `email_verification_tokens` | Tokens de verificación | user_id → profiles.id |
|
|
| `password_reset_tokens` | Tokens de reset | user_id → profiles.id |
|
|
| `security_events` | Eventos de seguridad | user_id → profiles.id |
|
|
|
|
---
|
|
|
|
## 6. Decisiones Arquitectónicas
|
|
|
|
### ADR-001: No Supabase
|
|
|
|
**Contexto:** El diseño inicial consideraba Supabase como opción de BaaS.
|
|
|
|
**Decisión:** Implementar autenticación personalizada con NestJS + PostgreSQL.
|
|
|
|
**Razones:**
|
|
1. Mayor control sobre lógica de autenticación
|
|
2. Sin dependencia de servicios externos
|
|
3. Flexibilidad para multi-tenant personalizado
|
|
4. RLS implementado directamente en PostgreSQL
|
|
|
|
**Consecuencias:**
|
|
- Mayor código de autenticación a mantener
|
|
- Mayor flexibilidad en personalización
|
|
- Sin costos de servicio externo
|
|
|
|
### ADR-002: Patrón de Dos Capas
|
|
|
|
**Contexto:** Necesidad de separar credenciales de datos de perfil.
|
|
|
|
**Decisión:** Usar `auth.users` para credenciales y `auth_management.profiles` para datos de negocio.
|
|
|
|
**Razones:**
|
|
1. Principio de responsabilidad única
|
|
2. Seguridad mejorada (aislamiento de credenciales)
|
|
3. Facilidad de extensión del perfil
|
|
4. Compatibilidad con patrones estándar de la industria
|
|
|
|
---
|
|
|
|
## 7. Constantes de Backend
|
|
|
|
```typescript
|
|
// apps/backend/src/shared/constants/database.constants.ts
|
|
|
|
export const DB_SCHEMAS = {
|
|
AUTH: 'auth_management', // Gestión de autenticación
|
|
AUTH_BASE: 'auth', // Credenciales base
|
|
// ... otros schemas
|
|
};
|
|
|
|
export const DB_TABLES = {
|
|
AUTH: {
|
|
USERS: 'users',
|
|
PROFILES: 'profiles',
|
|
USER_ROLES: 'user_roles',
|
|
ROLES: 'roles',
|
|
// ... resto de tablas
|
|
},
|
|
AUTH_BASE: {
|
|
USERS: 'users',
|
|
},
|
|
};
|
|
```
|
|
|
|
---
|
|
|
|
## 8. Ejemplo de Policies RLS
|
|
|
|
```sql
|
|
-- Solo el usuario puede ver su propio perfil
|
|
CREATE POLICY "Users can view own profile"
|
|
ON auth_management.profiles
|
|
FOR SELECT
|
|
TO authenticated
|
|
USING (id = current_setting('request.jwt.sub')::uuid);
|
|
|
|
-- Solo admins pueden ver todos los perfiles
|
|
CREATE POLICY "Admins can view all profiles"
|
|
ON auth_management.profiles
|
|
FOR SELECT
|
|
TO authenticated
|
|
USING (
|
|
EXISTS (
|
|
SELECT 1 FROM auth_management.user_roles ur
|
|
JOIN auth_management.roles r ON ur.role_id = r.id
|
|
WHERE ur.profile_id = current_setting('request.jwt.sub')::uuid
|
|
AND r.name IN ('admin', 'super_admin')
|
|
)
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
## 9. Referencias
|
|
|
|
- `apps/database/ddl/schemas/auth/` - DDL del schema auth
|
|
- `apps/database/ddl/schemas/auth_management/` - DDL del schema auth_management
|
|
- `apps/backend/src/modules/auth/` - Código de autenticación
|
|
- `orchestration/inventarios/DATABASE_INVENTORY.yml` - Inventario de BD
|
|
- `orchestration/agentes/database-auditor/audit-2025-12-14/07-REPORTE-CORRECCIONES-P0.md` - Auditoría P0
|
|
|
|
---
|
|
|
|
## 10. Historial de Cambios
|
|
|
|
| Fecha | Cambio | Autor |
|
|
|-------|--------|-------|
|
|
| 2025-12-14 | Documento inicial, clarificación de no-Supabase | AUDIT-DB-001 |
|
|
|
|
---
|
|
|
|
**Estado:** Este documento es la fuente de verdad para la arquitectura de autenticación de GAMILIT.
|