Changes include: - Updated architecture documentation - Enhanced module definitions (OQI-001 to OQI-008) - ML integration documentation updates - Trading strategies documentation - Orchestration and inventory updates - Docker configuration updates 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
15 KiB
15 KiB
| id | title | type | status | priority | module | epic | version | created_date | updated_date |
|---|---|---|---|---|---|---|---|---|---|
| RF-AUTH-005 | Gestion de Sesiones | Requirement | Done | Alta | auth | OQI-001 | 1.0 | 2025-12-05 | 2026-01-04 |
RF-AUTH-005: Gestión de Sesiones
Version: 1.0.0 Fecha: 2025-12-05 Estado: ✅ Implementado Prioridad: P0 (Crítica) Épica: OQI-001
Descripción
El sistema debe gestionar sesiones de usuario de forma segura, permitiendo múltiples dispositivos simultáneos, visualización de sesiones activas, y capacidad de revocar acceso de forma individual o global.
Objetivo de Negocio
- Permitir acceso desde múltiples dispositivos
- Dar control al usuario sobre sus sesiones
- Detectar y prevenir accesos no autorizados
- Cumplir con regulaciones de seguridad
Requisitos Funcionales
RF-AUTH-005.1: Creación de Sesión
DEBE:
- Crear sesión al login exitoso
- Registrar información del dispositivo
- Registrar IP y ubicación aproximada
- Generar refresh token único
- Establecer tiempo de expiración (7 días)
RF-AUTH-005.2: Tokens JWT
DEBE:
- Access token con TTL corto (15 min)
- Refresh token con TTL largo (7 días)
- Refresh token rotativo (nuevo en cada refresh)
- Invalidar refresh token anterior al rotar
- Almacenar hash del refresh token en DB
RF-AUTH-005.3: Visualización de Sesiones
DEBE:
- Listar todas las sesiones activas
- Mostrar dispositivo, navegador, SO
- Mostrar IP y ubicación
- Mostrar fecha de último acceso
- Identificar sesión actual
RF-AUTH-005.4: Revocación de Sesiones
DEBE:
- Permitir cerrar sesión individual
- Permitir cerrar todas las sesiones
- Cerrar sesiones al cambiar contraseña
- Cerrar sesiones al desactivar cuenta
RF-AUTH-005.5: Detección de Anomalías
DEBE:
- Detectar login desde nueva ubicación
- Detectar login desde nuevo dispositivo
- Notificar al usuario de accesos sospechosos
- Registrar en audit log
Estructura de Sesión
Modelo de Datos
CREATE TABLE sessions (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
refresh_token_hash VARCHAR(255) NOT NULL,
device_info JSONB NOT NULL,
ip_address INET,
location JSONB,
user_agent TEXT,
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMPTZ DEFAULT NOW(),
last_activity TIMESTAMPTZ DEFAULT NOW(),
expires_at TIMESTAMPTZ NOT NULL,
revoked_at TIMESTAMPTZ,
revoked_reason VARCHAR(100)
);
CREATE INDEX idx_sessions_user_id ON sessions(user_id);
CREATE INDEX idx_sessions_token_hash ON sessions(refresh_token_hash);
CREATE INDEX idx_sessions_expires ON sessions(expires_at) WHERE is_active = TRUE;
Device Info Structure
interface DeviceInfo {
type: 'desktop' | 'mobile' | 'tablet';
browser: string;
browserVersion: string;
os: string;
osVersion: string;
isMobile: boolean;
deviceId?: string; // Fingerprint opcional
}
Location Structure
interface LocationInfo {
country: string;
countryCode: string;
region: string;
city: string;
timezone: string;
// Aproximado por IP, no GPS
}
Flujo de Tokens
Login y Generación
Usuario Frontend Backend DB
│ │ │ │
│─── Login ───────────────▶│ │ │
│ │ │ │
│ │─── POST /auth/login ────▶│ │
│ │ + User-Agent │ │
│ │ + IP │ │
│ │ │ │
│ │ │─── Validate creds ─────│
│ │ │ │
│ │ │─── Parse device info ──│
│ │ │─── Get location by IP ─│
│ │ │ │
│ │ │─── Generate tokens ────│
│ │ │ accessToken (15m) │
│ │ │ refreshToken (7d) │
│ │ │ │
│ │ │─── INSERT session ────▶│
│ │ │ refresh_token_hash │
│ │ │ device_info │
│ │ │ ip, location │
│ │ │ │
│ │◀── 200 OK ───────────────│ │
│ │ { accessToken, │ │
│ │ refreshToken, │ │
│ │ expiresIn } │ │
│ │ │ │
│◀── Store tokens ─────────│ │ │
Refresh Token Rotation
Frontend Backend DB
│ │ │
│─── POST /auth/refresh ──▶│ │
│ { refreshToken } │ │
│ │ │
│ │─── Hash token ─────────│
│ │ │
│ │─── Find session ──────▶│
│ │◀── session data ───────│
│ │ │
│ │─── Validate: │
│ │ - Token matches │
│ │ - Not expired │
│ │ - Session active │
│ │ │
│ │─── Generate NEW tokens │
│ │ newAccessToken │
│ │ newRefreshToken │
│ │ │
│ │─── UPDATE session ────▶│
│ │ new token_hash │
│ │ last_activity │
│ │ │
│◀── 200 OK ───────────────│ │
│ { accessToken, │ │
│ refreshToken } │ │
Gestión de Sesiones UI
Lista de Sesiones
┌─────────────────────────────────────────────────────────────────────────┐
│ Sesiones Activas │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 🖥️ Windows · Chrome 120 [Sesión actual] │
│ Ciudad de México, México │
│ Último acceso: Ahora │
│ IP: 189.xxx.xxx.xxx │
│ │
│ ───────────────────────────────────────────────────────────────────── │
│ │
│ 📱 iPhone · Safari 17 [Revocar] │
│ Guadalajara, México │
│ Último acceso: Hace 2 horas │
│ IP: 187.xxx.xxx.xxx │
│ │
│ ───────────────────────────────────────────────────────────────────── │
│ │
│ 💻 MacOS · Firefox 121 [Revocar] │
│ Monterrey, México │
│ Último acceso: Hace 3 días │
│ IP: 201.xxx.xxx.xxx │
│ │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ⚠️ ¿No reconoces alguna sesión? │
│ [Cerrar todas las demás sesiones] │
│ │
└─────────────────────────────────────────────────────────────────────────┘
API Endpoints
Listar Sesiones
GET /auth/sessions
Authorization: Bearer {accessToken}
Response 200:
{
"sessions": [
{
"id": "uuid",
"device": {
"type": "desktop",
"browser": "Chrome",
"os": "Windows"
},
"location": {
"city": "Ciudad de México",
"country": "México"
},
"ipAddress": "189.xxx.xxx.xxx",
"lastActivity": "2025-12-05T10:00:00Z",
"createdAt": "2025-12-01T08:00:00Z",
"isCurrent": true
}
]
}
Revocar Sesión
DELETE /auth/sessions/{sessionId}
Authorization: Bearer {accessToken}
Response 200:
{
"message": "Sesión cerrada exitosamente"
}
Revocar Todas las Sesiones
DELETE /auth/sessions
Authorization: Bearer {accessToken}
Response 200:
{
"message": "Todas las demás sesiones han sido cerradas",
"revokedCount": 3
}
Detección de Anomalías
Triggers de Alerta
| Evento | Acción |
|---|---|
| Nuevo dispositivo | Email de notificación |
| Nueva ubicación (país) | Email + notificación in-app |
| Login desde IP en blacklist | Bloquear + notificar |
| Múltiples IPs en poco tiempo | Notificar + audit log |
| Login en horario inusual | Audit log |
Email de Nuevo Dispositivo
Asunto: Nuevo inicio de sesión en tu cuenta OrbiQuant
Hola {{firstName}},
Detectamos un nuevo inicio de sesión en tu cuenta:
Dispositivo: {{device}}
Ubicación: {{location}}
Fecha: {{date}}
IP: {{ip}}
Si fuiste tú, puedes ignorar este mensaje.
Si NO fuiste tú:
1. Cambia tu contraseña inmediatamente
2. Activa 2FA si no lo tienes
3. Revisa tus sesiones activas
[Asegurar mi cuenta]
Saludos,
Equipo OrbiQuant
Limpieza de Sesiones
Job Programado
// Ejecutar cada hora
async function cleanupExpiredSessions() {
await db.query(`
UPDATE sessions
SET is_active = FALSE,
revoked_reason = 'expired'
WHERE expires_at < NOW()
AND is_active = TRUE
`);
}
Retención de Datos
| Tipo de Sesión | Retención |
|---|---|
| Activa | Hasta expiración o revocación |
| Revocada | 30 días (audit) |
| Expirada | 30 días (audit) |
Configuración
const sessionConfig = {
accessToken: {
expiresIn: '15m',
algorithm: 'RS256',
},
refreshToken: {
expiresIn: '7d',
algorithm: 'RS256',
},
session: {
maxPerUser: 10, // Máximo sesiones simultáneas
inactivityTimeout: '30d', // Auto-revoke si inactiva
},
};
Manejo de Errores
| Error | Código | Mensaje Usuario |
|---|---|---|
| Token expirado | 401 | Sesión expirada, inicia sesión |
| Token inválido | 401 | Token inválido |
| Sesión revocada | 401 | Sesión cerrada, inicia sesión |
| Sesión no encontrada | 404 | Sesión no encontrada |
| No autorizado | 403 | No puedes cerrar esta sesión |
| Límite de sesiones | 400 | Máximo de sesiones alcanzado |
Seguridad
Almacenamiento de Tokens
- Access token: Solo en memoria (no localStorage)
- Refresh token: httpOnly cookie o secure storage
- Hash del refresh token en DB (no el token en sí)
Protección de Refresh
- Rotación en cada uso
- Detección de reuso (posible robo)
- Binding a device fingerprint (opcional)
Reuse Detection
Si un refresh token ya usado se presenta:
- Invalidar TODAS las sesiones del usuario
- Notificar al usuario
- Registrar en security log
- Posible cuenta comprometida
Criterios de Aceptación
- Sesión se crea al login exitoso
- Device info se detecta correctamente
- Ubicación por IP funciona
- Access token expira en 15 minutos
- Refresh token renueva correctamente
- Usuario puede ver sesiones activas
- Usuario puede revocar sesión individual
- Usuario puede revocar todas las sesiones
- Notificación de nuevo dispositivo funciona
- Sesiones se limpian al expirar