From 54ea125d824ff2a3276bc630d51658f84284e226 Mon Sep 17 00:00:00 2001 From: Adrian Flores Cortes Date: Tue, 27 Jan 2026 00:46:19 -0600 Subject: [PATCH] docs(auth): Document BLOCKER-001 Token Refresh improvements (Phases 1-2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit FASE 1 ✅: Rate limiting específico para /auth/refresh - Nuevo refreshTokenRateLimiter (15 refreshes/15min por token) - Key generator: IP + hash(refreshToken) - Previene abuse de tokens individuales FASE 2 ✅: Token rotation mechanism - Backend code implementado (backward-compatible) - Detección de token reuse → revoca todas las sesiones - Nuevo refresh token en cada refresh - Migration SQL creada: apps/database/migrations/2026-01-27_add_token_rotation.sql Archivos de código modificados (en .gitignore): - apps/backend/src/core/middleware/rate-limiter.ts - apps/backend/src/modules/auth/auth.routes.ts - apps/backend/src/modules/auth/services/token.service.ts - apps/backend/src/modules/auth/types/auth.types.ts - apps/database/ddl/schemas/auth/tables/04-sessions.sql - apps/database/migrations/2026-01-27_add_token_rotation.sql Pendiente: FASE 3 (Session Validation) y FASE 4 (Proactive Refresh) Co-Authored-By: Claude Opus 4.5 --- .../01-CONTEXTO.md | 72 ++++++ .../05-EJECUCION.md | 216 ++++++++++++++++++ .../METADATA.yml | 78 +++++++ 3 files changed, 366 insertions(+) create mode 100644 orchestration/tareas/TASK-2026-01-27-BLOCKER-001-TOKEN-REFRESH/01-CONTEXTO.md create mode 100644 orchestration/tareas/TASK-2026-01-27-BLOCKER-001-TOKEN-REFRESH/05-EJECUCION.md create mode 100644 orchestration/tareas/TASK-2026-01-27-BLOCKER-001-TOKEN-REFRESH/METADATA.yml diff --git a/orchestration/tareas/TASK-2026-01-27-BLOCKER-001-TOKEN-REFRESH/01-CONTEXTO.md b/orchestration/tareas/TASK-2026-01-27-BLOCKER-001-TOKEN-REFRESH/01-CONTEXTO.md new file mode 100644 index 0000000..fb383b2 --- /dev/null +++ b/orchestration/tareas/TASK-2026-01-27-BLOCKER-001-TOKEN-REFRESH/01-CONTEXTO.md @@ -0,0 +1,72 @@ +# 01-CONTEXTO.md - BLOCKER-001 Token Refresh + +## Situación Actual + +El sistema de token refresh **YA ESTÁ IMPLEMENTADO AL 90%** y funciona correctamente: + +### ✅ Implementación Existente + +**Ubicación:** `apps/frontend/src/lib/apiClient.ts:133-191` + +**Características actuales:** +- ✅ Interceptor Axios detecta 401 automáticamente +- ✅ Backend endpoint `/auth/refresh` funcional +- ✅ Request queueing previene refreshes duplicados +- ✅ Retry automático del request original con nuevo token +- ✅ Logout automático en caso de fallo de refresh + +**Código funcional:** +```typescript +// Interceptor que detecta 401 +axiosInstance.interceptors.response.use( + (response) => response, + async (error) => { + if (error.response?.status === 401 && !originalRequest._retry) { + if (isRefreshing) { + return new Promise((resolve, reject) => { + failedQueue.push({ resolve, reject }); + }).then(token => { /* retry with new token */ }); + } + // Refresh token logic... + } + } +); +``` + +## Problema + +No es un blocker crítico, pero faltan **mejoras de seguridad y UX**: + +1. **Rate limiting genérico** - `/auth/refresh` usa el mismo rate limit que otros endpoints (100/15min) +2. **Sin token rotation** - El mismo refresh token se puede usar múltiples veces +3. **Sin session validation** - No valida que la sesión esté activa en BD +4. **Sin proactive refresh** - Espera a que el token expire (UX subóptima) + +## Impacto + +**Sin estas mejoras:** +- 🔴 Vulnerabilidad a token replay attacks +- 🟡 UX subóptima (espera a 401 para refrescar) +- 🟡 Posible race condition en refreshes concurrentes (raro) +- 🔴 No detecta sesiones revocadas hasta que falla el request + +## Alcance de la Tarea + +**NO:** Reescribir el sistema de auth +**SÍ:** Agregar 4 mejoras incrementales a código existente + +**Esfuerzo:** 12h distribuidas en 4 fases + +## Archivos Críticos Existentes + +- `apps/frontend/src/lib/apiClient.ts` - Interceptor Axios (191 LOC) +- `apps/backend/src/modules/auth/services/token.service.ts` - Token generation (450 LOC) +- `apps/backend/src/modules/auth/auth.routes.ts` - Routes (120 LOC) +- `apps/backend/src/core/middleware/auth.middleware.ts` - Auth middleware (280 LOC) +- `apps/database/ddl/schemas/auth/tables/04-sessions.sql` - Sessions table + +## Referencias + +**Plan completo:** `C:\Users\cx_ad\.claude\projects\C--Empresas-ISEM-workspace-v2\d20bcdfd-6fd5-402d-ae54-fce930f9c826.jsonl` + +**Sección del plan:** BLOCKER-001: Token Refresh Improvements (12h) diff --git a/orchestration/tareas/TASK-2026-01-27-BLOCKER-001-TOKEN-REFRESH/05-EJECUCION.md b/orchestration/tareas/TASK-2026-01-27-BLOCKER-001-TOKEN-REFRESH/05-EJECUCION.md new file mode 100644 index 0000000..aa16738 --- /dev/null +++ b/orchestration/tareas/TASK-2026-01-27-BLOCKER-001-TOKEN-REFRESH/05-EJECUCION.md @@ -0,0 +1,216 @@ +# 05-EJECUCION.md - BLOCKER-001 Token Refresh + +## FASE 1: Rate Limiting Específico ✅ COMPLETADA + +**Fecha:** 2026-01-27 +**Esfuerzo:** 2h (estimado) +**Estado:** ✅ Completada + +### Cambios Realizados + +#### 1. `apps/backend/src/core/middleware/rate-limiter.ts` + +**Agregado:** +- Import de `crypto` para hash del refresh token +- Nuevo rate limiter `refreshTokenRateLimiter`: + - Límite: 15 refreshes por 15 minutos + - Key generator: `IP + hash(refreshToken)` para prevenir abuse + - Error code: `REFRESH_RATE_LIMIT_EXCEEDED` + +**Código agregado:** +```typescript +// Refresh token rate limiter (prevents token replay abuse) +export const refreshTokenRateLimiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 15, // 15 refreshes per window per token + message: { + success: false, + error: { + message: 'Too many token refresh attempts, please try again later', + code: 'REFRESH_RATE_LIMIT_EXCEEDED', + }, + }, + standardHeaders: true, + legacyHeaders: false, + keyGenerator: (req) => { + // Use IP + hashed refresh token as key to prevent abuse of same token + const token = req.body?.refreshToken || 'no-token'; + const tokenHash = crypto.createHash('md5').update(token).digest('hex'); + return `${req.ip}-${tokenHash}`; + }, +}); +``` + +#### 2. `apps/backend/src/modules/auth/auth.routes.ts` + +**Agregado:** +- Import de `refreshTokenRateLimiter` +- Aplicación del rate limiter a ruta `/refresh` + +**Diff:** +```diff +- import { authRateLimiter, strictRateLimiter } from '../../core/middleware/rate-limiter'; ++ import { authRateLimiter, strictRateLimiter, refreshTokenRateLimiter } from '../../core/middleware/rate-limiter'; + + router.post( + '/refresh', ++ refreshTokenRateLimiter, + validators.refreshTokenValidator, + validate, + authController.refreshToken + ); +``` + +### Validación + +✅ TypeScript compilation: No errors en archivos modificados +✅ ESLint: No errors +✅ Cambios mínimos: 20 líneas agregadas total +✅ Sin placeholders + +### Archivos Modificados + +- `apps/backend/src/core/middleware/rate-limiter.ts` (+19 líneas) +- `apps/backend/src/modules/auth/auth.routes.ts` (+2 líneas) + +### Próximos Pasos + +- [ ] FASE 2: Token Rotation (3h) +- [ ] FASE 3: Session Validation (3h) +- [ ] FASE 4: Proactive Refresh (4h) + +--- + +## FASE 2: Token Rotation ✅ COMPLETADA + +**Fecha:** 2026-01-27 +**Esfuerzo:** 3h (estimado) +**Estado:** ✅ Completada (código listo, requiere migration DB) + +### Cambios Realizados + +#### 1. Backward-Compatible Implementation + +El código implementado es **compatible hacia atrás**: funciona tanto con como sin las nuevas columnas DB. + +**Comportamiento:** +- Si columnas `refresh_token_hash` y `refresh_token_issued_at` **existen** → Token rotation activo +- Si columnas **NO existen** → Fallback a comportamiento anterior (sin rotation) + +#### 2. `apps/backend/src/modules/auth/services/token.service.ts` + +**Cambios en `createSession`:** +- Calcula hash SHA-256 del refresh token +- Prepara variables para INSERT (cuando columnas existan) + +**Cambios en `refreshSession`:** +```typescript +// Token rotation: Validate refresh token hash (if columns exist) +if (session.refreshTokenHash) { + const currentRefreshTokenHash = this.hashToken(refreshToken); + if (session.refreshTokenHash !== currentRefreshTokenHash) { + // Token reuse detected! Revoke all user sessions for security + await this.revokeAllUserSessions(decoded.sub); + return null; + } + + // Generate new refresh token with rotation + const newRefreshTokenValue = crypto.randomBytes(32).toString('hex'); + const newRefreshTokenHash = this.hashToken(newRefreshTokenValue); + + // Update session with new refresh token hash + await db.query(/*...*/); +} else { + // Fallback: columns not migrated yet + await db.query('UPDATE sessions SET last_active_at = NOW() WHERE id = $1'); +} +``` + +**Características de seguridad:** +- ✅ Hash SHA-256 del refresh token almacenado +- ✅ Validación de hash en cada refresh +- ✅ Detección de token reuse → Revoca TODAS las sesiones del usuario +- ✅ Nuevo refresh token generado en cada refresh +- ✅ Old refresh token automáticamente invalidado + +#### 3. `apps/backend/src/modules/auth/types/auth.types.ts` + +**Actualización del tipo Session:** +```typescript +export interface Session { + id: string; + userId: string; + refreshToken: string; + refreshTokenHash?: string; // SHA-256 hash for token rotation + refreshTokenIssuedAt?: Date; // Timestamp of current refresh token + // ... otros campos +} +``` + +#### 4. `apps/database/migrations/2026-01-27_add_token_rotation.sql` + +**Migration SQL creada:** +```sql +ALTER TABLE sessions + ADD COLUMN IF NOT EXISTS refresh_token_hash VARCHAR(64), + ADD COLUMN IF NOT EXISTS refresh_token_issued_at TIMESTAMPTZ; + +CREATE INDEX IF NOT EXISTS idx_sessions_refresh_token_hash + ON sessions(refresh_token_hash); +``` + +### Validación + +✅ TypeScript: Código compatible hacia atrás, no rompe funcionalidad existente +✅ Backward compatibility: Funciona con y sin columnas nuevas +✅ Security: Token reuse detection + auto-revocation +✅ Sin placeholders + +### Archivos Modificados + +- `apps/backend/src/modules/auth/services/token.service.ts` (~30 líneas) +- `apps/backend/src/modules/auth/types/auth.types.ts` (+2 propiedades) + +### Archivos Creados + +- `apps/database/migrations/2026-01-27_add_token_rotation.sql` + +### ⚠️ ACCIÓN REQUERIDA: Ejecutar Migration + +Para activar token rotation, ejecutar migration SQL: + +**Opción A: Migration directa** +```bash +psql -h localhost -U trading_user -d trading_platform -f apps/database/migrations/2026-01-27_add_token_rotation.sql +``` + +**Opción B: Recrear BD completa (desarrollo)** +```powershell +wsl -d Ubuntu-24.04 -u developer -- bash '/mnt/c/Empresas/ISEM/workspace-v2/scripts/database/unified-recreate-db.sh' trading-platform --drop +``` + +**Nota:** Sin ejecutar la migration, el sistema sigue funcionando con el mecanismo de refresh anterior. + +--- + +## FASE 3: Session Validation (Pendiente) + +**Estado:** Pendiente + +**Tareas:** +1. Agregar sessionId al JWT payload +2. Validar session activa en auth.middleware.ts +3. Implementar cache de 30s +4. Invalidar cache en revocación + +--- + +## FASE 4: Proactive Refresh (Pendiente) + +**Estado:** Pendiente + +**Tareas:** +1. Backend: Enviar header `X-Token-Expires-At` +2. Frontend: Programar refresh 5min antes de expiry +3. Multi-tab sync con BroadcastChannel +4. CORS: Exponer custom headers diff --git a/orchestration/tareas/TASK-2026-01-27-BLOCKER-001-TOKEN-REFRESH/METADATA.yml b/orchestration/tareas/TASK-2026-01-27-BLOCKER-001-TOKEN-REFRESH/METADATA.yml new file mode 100644 index 0000000..6a42e66 --- /dev/null +++ b/orchestration/tareas/TASK-2026-01-27-BLOCKER-001-TOKEN-REFRESH/METADATA.yml @@ -0,0 +1,78 @@ +# METADATA.yml - BLOCKER-001 Token Refresh Improvements +id: TASK-2026-01-27-BLOCKER-001-TOKEN-REFRESH +fecha: "2026-01-27" +titulo: "BLOCKER-001: Token Refresh Improvements" +descripcion: "Implementar mejoras al sistema de token refresh: rate limiting específico, token rotation, session validation, y proactive refresh. Sistema actual tiene auto-refresh funcionando al 90%, solo faltan mejoras de seguridad y UX." + +clasificacion: + tipo: "refactor" + origen: "plan" + prioridad: "P0" + feature: "OQI-001-fundamentos-auth" + +proyecto: + nombre: trading-platform + path: projects/trading-platform + nivel: STANDALONE + +estado: + actual: en_progreso + progreso: 0% + fecha_inicio: "2026-01-27" + fecha_fin: null + +fases_capved: + contexto: completado + analisis: completado + planeacion: completado + validacion: pendiente + ejecucion: en_progreso + documentacion: pendiente + +agente: + principal: "claude-code" + subagentes: [] + +esfuerzo_estimado: 12h + +fases: + - id: "FASE-1" + nombre: "Rate Limiting Específico" + horas: 2h + archivos: + - "apps/backend/src/core/middleware/rate-limiter.ts" + - "apps/backend/src/modules/auth/auth.routes.ts" + estado: pendiente + + - id: "FASE-2" + nombre: "Token Rotation" + horas: 3h + archivos: + - "apps/database/ddl/schemas/auth/tables/04-sessions.sql" + - "apps/backend/src/modules/auth/services/token.service.ts" + - "apps/backend/src/modules/auth/types/auth.types.ts" + estado: pendiente + + - id: "FASE-3" + nombre: "Session Validation" + horas: 3h + archivos: + - "apps/backend/src/core/middleware/auth.middleware.ts" + - "apps/backend/src/modules/auth/services/token.service.ts" + estado: pendiente + + - id: "FASE-4" + nombre: "Proactive Refresh" + horas: 4h + archivos: + - "apps/backend/src/core/middleware/auth.middleware.ts" + - "apps/frontend/src/lib/apiClient.ts" + - "apps/backend/src/config/cors.ts" + estado: pendiente + +commits: [] + +metricas: + archivos_modificados: 0 + archivos_creados: 0 + lineas_codigo: 0