trading-platform/docs/02-definicion-modulos/OQI-001-fundamentos-auth/historias-usuario/US-AUTH-011-password-reset.md
rckrdmrd c1b5081208 feat(ml): Complete FASE 11 - BTCUSD update and comprehensive documentation alignment
ML Engine Updates:
- Updated BTCUSD with Polygon API data (2024-2025): 215,699 new records
- Re-trained all ML models: Attention (R²: 0.223), Base, Metamodel (87.3% confidence)
- Backtest results: +176.71R profit with aggressive_filter strategy

Documentation Consolidation:
- Created docs/99-analisis/_MAP.md index with 13 new analysis documents
- Consolidated inventories: removed duplicates from orchestration/inventarios/
- Updated ML_INVENTORY.yml with BTCUSD metrics and training results
- Added execution reports: FASE11-BTCUSD, correction issues, alignment validation

Architecture & Integration:
- Updated all module documentation with NEXUS v3.4 frontmatter
- Fixed _MAP.md indexes across all folders
- Updated orchestration plans and traces

Files: 229 changed, 5064 insertions(+), 1872 deletions(-)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 09:31:29 -06:00

22 KiB

id title type status priority epic story_points created_date updated_date
US-AUTH-011 Recuperacion de Contrasena User Story To Do Alta OQI-001 3 2025-12-05 2026-01-04

US-AUTH-011: Recuperación de Contraseña

Version: 1.0.0 Fecha: 2025-12-05 Estado: Pendiente Story Points: 3 Prioridad: P0 (Crítica) Épica: OQI-001


Historia de Usuario

Como usuario de Trading Platform que olvidó su contraseña Quiero poder recuperar el acceso a mi cuenta Para poder volver a iniciar sesión sin necesidad de crear una nueva cuenta


Criterios de Aceptación

Dado que estoy en la página de login Cuando veo el formulario Entonces debería ver un link "¿Olvidaste tu contraseña?" Y debería estar claramente visible

AC-002: Formulario de solicitud

Dado que hago click en "¿Olvidaste tu contraseña?" Cuando accedo a la página de recuperación Entonces debería ver:

  • Título "Recupera tu contraseña"
  • Campo para ingresar email
  • Botón "Enviar instrucciones"
  • Link para volver a login

AC-003: Validación de email

Dado que estoy en el formulario de recuperación Cuando ingreso un email inválido Entonces debería ver mensaje "Email inválido" Y el botón debería estar deshabilitado

AC-004: Email enviado (cuenta existe)

Dado que ingresé un email registrado Cuando hago click en "Enviar instrucciones" Entonces debería:

  1. Ver mensaje "Si el email está registrado, recibirás instrucciones"
  2. Recibir un email con link de recuperación
  3. El link debería expirar en 1 hora

AC-005: Email no registrado

Dado que ingresé un email no registrado Cuando hago click en "Enviar instrucciones" Entonces debería ver el mismo mensaje genérico Y NO debería revelar que el email no existe (seguridad)

AC-006: Contenido del email

Dado que solicité recuperación de contraseña Cuando reviso mi email Entonces debería recibir un email con:

  • Asunto: "Recupera tu contraseña de Trading Platform"
  • Saludo personalizado con mi nombre
  • Explicación clara del proceso
  • Botón/link de "Restablecer contraseña"
  • Nota de que expira en 1 hora
  • Nota de ignorar si no solicité el cambio

Dado que recibí el email de recuperación Cuando hago click en el link Entonces debería ser redirigido a una página de "Nueva contraseña" Y el token debería validarse automáticamente

AC-008: Formulario de nueva contraseña

Dado que accedí con un token válido Cuando veo el formulario Entonces debería ver:

  • Campo "Nueva contraseña"
  • Campo "Confirmar nueva contraseña"
  • Indicadores de fortaleza de contraseña
  • Botón "Guardar nueva contraseña"

AC-009: Validación de nueva contraseña

Dado que estoy ingresando nueva contraseña Cuando ingreso una contraseña débil Entonces debería ver los mismos requisitos que en registro:

  • / Mínimo 8 caracteres
  • / Al menos una mayúscula
  • / Al menos una minúscula
  • / Al menos un número
  • / Al menos un carácter especial

AC-010: Contraseñas no coinciden

Dado que las contraseñas no coinciden Cuando intento guardar Entonces debería ver mensaje "Las contraseñas no coinciden"

AC-011: Cambio exitoso

Dado que ingresé una contraseña válida Cuando hago click en "Guardar nueva contraseña" Entonces debería:

  1. Ver mensaje "Contraseña actualizada exitosamente"
  2. Invalidar el token de recuperación
  3. Cerrar todas las sesiones activas
  4. Ser redirigido a login
  5. Poder iniciar sesión con la nueva contraseña

AC-012: Token expirado

Dado que pasó más de 1 hora desde la solicitud Cuando intento usar el link de recuperación Entonces debería ver mensaje:

  • "Este link ha expirado. Solicita uno nuevo" Y debería ver botón "Solicitar nuevo link"

AC-013: Token ya usado

Dado que ya usé un token para cambiar contraseña Cuando intento usar el mismo link nuevamente Entonces debería ver mensaje:

  • "Este link ya fue usado" Y debería ver link a login

AC-014: Token inválido

Dado que el token fue manipulado o es inválido Cuando intento acceder Entonces debería ver mensaje:

  • "Link inválido. Solicita uno nuevo"

AC-015: Rate limiting

Dado que solicité 3 recuperaciones en 1 hora Cuando intento solicitar otra Entonces debería ver mensaje:

  • "Demasiadas solicitudes. Intenta en 1 hora"

Mockup

Paso 1: Solicitud de recuperación
┌─────────────────────────────────────────────────────────────┐
│                                                             │
│              🔐 Recupera tu contraseña                     │
│                                                             │
│  Ingresa tu email y te enviaremos instrucciones            │
│  para recuperar tu contraseña.                              │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ Email                                                │   │
│  │ ┌─────────────────────────────────────────────────┐ │   │
│  │ │ usuario@example.com                             │ │   │
│  │ └─────────────────────────────────────────────────┘ │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │           Enviar instrucciones                       │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  ← Volver a inicio de sesión                               │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Paso 2: Confirmación
┌─────────────────────────────────────────────────────────────┐
│                                                             │
│              ✉️ Revisa tu email                            │
│                                                             │
│  Si el email está registrado, recibirás instrucciones      │
│  para recuperar tu contraseña.                              │
│                                                             │
│  Revisa también tu carpeta de spam.                        │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │           Volver a inicio de sesión                  │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  ¿No recibiste el email? Intenta de nuevo en 5 minutos    │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Email de recuperación:
┌─────────────────────────────────────────────────────────────┐
│                                                             │
│  De: Trading Platform <noreply@trading.com>                     │
│  Para: usuario@example.com                                  │
│  Asunto: Recupera tu contraseña de Trading Platform               │
│                                                             │
│  ─────────────────────────────────────────────────────     │
│                                                             │
│  Hola Juan,                                                 │
│                                                             │
│  Recibimos una solicitud para recuperar tu contraseña.     │
│                                                             │
│  Haz click en el botón de abajo para crear una nueva:      │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │           Restablecer mi contraseña                  │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  O copia este link en tu navegador:                        │
│  https://trading.com/reset-password?token=abc123...      │
│                                                             │
│  Este link expirará en 1 hora.                             │
│                                                             │
│  Si no solicitaste este cambio, puedes ignorar este        │
│  email. Tu contraseña no cambiará.                         │
│                                                             │
│  Saludos,                                                   │
│  El equipo de Trading Platform                                    │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Paso 3: Nueva contraseña
┌─────────────────────────────────────────────────────────────┐
│                                                             │
│              🔒 Crea una nueva contraseña                  │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ Nueva contraseña                                     │   │
│  │ ┌─────────────────────────────────────────────────┐ │   │
│  │ │ ••••••••••••                              👁   │ │   │
│  │ └─────────────────────────────────────────────────┘ │   │
│  │                                                     │   │
│  │ ✅ Mínimo 8 caracteres                              │   │
│  │ ✅ Una mayúscula                                    │   │
│  │ ✅ Una minúscula                                    │   │
│  │ ✅ Un número                                        │   │
│  │ ❌ Un carácter especial                             │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ Confirmar nueva contraseña                           │   │
│  │ ┌─────────────────────────────────────────────────┐ │   │
│  │ │ ••••••••••••                              👁   │ │   │
│  │ └─────────────────────────────────────────────────┘ │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │         Guardar nueva contraseña                     │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Paso 4: Éxito
┌─────────────────────────────────────────────────────────────┐
│                                                             │
│              ✅ Contraseña actualizada                     │
│                                                             │
│  Tu contraseña ha sido actualizada exitosamente.           │
│  Por seguridad, cerramos todas tus sesiones activas.       │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │           Iniciar sesión                             │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Tareas Técnicas

Database (DB)

  • Tabla password_reset_tokens:
    CREATE TABLE password_reset_tokens (
      id UUID PRIMARY KEY,
      user_id UUID REFERENCES users(id),
      token VARCHAR(255) UNIQUE NOT NULL,
      expires_at TIMESTAMP NOT NULL,
      used_at TIMESTAMP,
      ip_address VARCHAR(45),
      created_at TIMESTAMP DEFAULT NOW(),
      INDEX idx_token_expires (token, expires_at)
    );
    
  • Tabla password_reset_rate_limits:
    CREATE TABLE password_reset_rate_limits (
      id UUID PRIMARY KEY,
      email VARCHAR(255) NOT NULL,
      ip_address VARCHAR(45),
      attempts INT DEFAULT 1,
      window_start TIMESTAMP DEFAULT NOW(),
      INDEX idx_email_window (email, window_start)
    );
    

Backend (BE)

  • Endpoint POST /api/v1/auth/password/forgot
    • Validar email
    • Rate limiting (3 por hora)
    • Generar token seguro (crypto.randomBytes)
    • Guardar token con expiración de 1 hora
    • Enviar email
    • Respuesta genérica (no revelar si existe)
  • Endpoint GET /api/v1/auth/password/reset/:token
    • Validar token existe y no expiró
    • Validar no fue usado
    • Devolver status del token
  • Endpoint POST /api/v1/auth/password/reset
    • Validar token
    • Validar nueva contraseña
    • Hash nueva contraseña
    • Actualizar password
    • Marcar token como usado
    • Invalidar todas las sesiones
    • Enviar email de confirmación
  • Service PasswordResetService
    • requestReset(email)
    • validateToken(token)
    • resetPassword(token, newPassword)
    • generateSecureToken()
  • Email templates (Nodemailer)
    • Template de recuperación
    • Template de confirmación
  • Tests unitarios (12 casos)
  • Tests de integración (8 escenarios)

Frontend (FE)

  • Página ForgotPassword.tsx
  • Página ResetPassword.tsx
  • Form validation con React Hook Form
  • Password strength indicator
  • Manejo de estados (loading, success, error)
  • Manejo de tokens expirados/inválidos
  • Tests con React Testing Library

Testing (QA)

  • E2E: Flujo completo de recuperación
  • E2E: Token expirado
  • E2E: Token ya usado
  • E2E: Token inválido
  • E2E: Rate limiting
  • E2E: Contraseña débil rechazada
  • Test de seguridad: Token debe ser criptográficamente seguro
  • Test de seguridad: No revelar si email existe
  • Performance: Email enviado en < 2s

Dependencias

  • Bloqueantes:
    • US-AUTH-001: Necesita usuarios con contraseñas
    • US-AUTH-002: Para el flujo de login posterior

Definition of Ready (DoR)

  • Mockups aprobados
  • Email templates diseñados
  • API contract definido
  • Estrategia de seguridad revisada
  • Rate limiting definido

Definition of Done (DoD)

  • Código implementado y revisado
  • Tests unitarios con 80%+ cobertura
  • Tests de integración pasando
  • Tests E2E implementados
  • Email templates funcionales
  • Rate limiting implementado
  • Tokens seguros (crypto.randomBytes)
  • No se revela existencia de emails
  • Logs implementados
  • Documentación actualizada
  • QA aprobado en staging
  • Deploy a producción exitoso

Notas Técnicas

Token Generation

import crypto from 'crypto';

// Generar token seguro
const token = crypto.randomBytes(32).toString('hex');
// Resultado: "a1b2c3d4e5f6..." (64 caracteres)

// Hash del token antes de guardar (opcional, extra seguridad)
const hashedToken = crypto
  .createHash('sha256')
  .update(token)
  .digest('hex');

Password Reset Flow

  1. Usuario solicita recuperación
  2. Backend verifica rate limits
  3. Backend genera token seguro
  4. Backend guarda token con expiración (1 hora)
  5. Backend envía email con link
  6. Usuario click en link
  7. Frontend valida token con backend
  8. Usuario ingresa nueva contraseña
  9. Backend valida token nuevamente
  10. Backend actualiza contraseña
  11. Backend marca token como usado
  12. Backend invalida sesiones activas
  13. Backend envía email de confirmación

Email Template (HTML)

<!DOCTYPE html>
<html>
<head>
  <style>
    /* Email styles */
  </style>
</head>
<body>
  <div style="max-width: 600px; margin: 0 auto;">
    <h1>Recupera tu contraseña</h1>
    <p>Hola {{userName}},</p>
    <p>Recibimos una solicitud para recuperar tu contraseña.</p>
    <a href="{{resetLink}}" style="display: inline-block; padding: 12px 24px; background: #007bff; color: white; text-decoration: none; border-radius: 4px;">
      Restablecer mi contraseña
    </a>
    <p>O copia este link: {{resetLink}}</p>
    <p>Este link expirará en 1 hora.</p>
    <p>Si no solicitaste este cambio, puedes ignorar este email.</p>
  </div>
</body>
</html>

Environment Variables

PASSWORD_RESET_TOKEN_EXPIRY=3600 # 1 hora en segundos
PASSWORD_RESET_RATE_LIMIT=3 # intentos por ventana
PASSWORD_RESET_RATE_WINDOW=3600 # 1 hora
FRONTEND_URL=https://trading.com

Security Best Practices

  1. Tokens seguros:

    • Usar crypto.randomBytes (no Math.random)
    • Mínimo 32 bytes (256 bits)
    • Considerar hashear antes de guardar
  2. No revelar información:

    • Mismo mensaje para email existente o no
    • No indicar si un email está registrado
  3. Rate limiting:

    • Máximo 3 intentos por hora por email
    • También limitar por IP
  4. Expiración:

    • Tokens expiran en 1 hora
    • Tokens de un solo uso
  5. Invalidar sesiones:

    • Al cambiar contraseña, cerrar todas las sesiones
    • Forzar nuevo login
  6. Logging:

    • Log de todas las solicitudes
    • Log de tokens usados/expirados
    • No loggear el token en texto plano
  7. Email de confirmación:

    • Notificar al usuario que su contraseña cambió
    • Incluir información de cuándo y desde dónde

Rate Limiting Implementation

async function checkRateLimit(email: string, ip: string) {
  const oneHourAgo = new Date(Date.now() - 3600000);

  const attempts = await db.passwordResetRateLimits.count({
    where: {
      email,
      window_start: { gte: oneHourAgo }
    }
  });

  if (attempts >= 3) {
    throw new Error('Too many password reset attempts');
  }

  // Registrar intento
  await db.passwordResetRateLimits.create({
    data: { email, ip_address: ip }
  });
}

Requerimientos Relacionados

Especificaciones Relacionadas