erp-core/docs/05-user-stories/mgn-001/US-MGN-001-001-002-renovar-token-jwt.md

9.4 KiB

US-MGN-001-001-002: Renovar Token JWT con Refresh Token

RF Asociado: RF-MGN-001-001 Módulo: MGN-001 - Fundamentos Epic: Autenticación y Seguridad Prioridad: P0 (MVP) Story Points: 3 Sprint: Sprint 1 Estado: Ready for Development Fecha: 2025-11-24


User Story

Como usuario autenticado del sistema, Quiero renovar mi token JWT automáticamente cuando expire, Para mantener mi sesión activa sin tener que volver a ingresar mis credenciales.


Descripción Detallada

Esta user story implementa el mecanismo de renovación de tokens JWT usando refresh tokens. Cuando el JWT (8 horas de vida) expira, el frontend usa el refresh token (30 días de vida) para obtener un nuevo JWT sin requerir login. Esto mejora la experiencia del usuario al evitar logouts inesperados durante el uso del sistema.

El refresh token se almacena en la tabla auth.sessions con información del dispositivo/navegador y puede ser revocado individualmente (logout en un dispositivo específico).


Criterios de Aceptación

Escenario 1: Renovación exitosa con refresh token válido

Dado que mi JWT ha expirado pero tengo un refresh token válido, Cuando el frontend detecta el JWT expirado y envía el refresh token, Entonces el sistema genera un nuevo JWT y refresh token, los retorna al frontend, y la sesión continúa sin interrupción.

Escenario 2: Refresh token inválido o revocado

Dado que mi refresh token fue revocado o no existe en la base de datos, Cuando intento renovar mi JWT, Entonces el sistema retorna error 401 "Sesión expirada" y me redirige al login.

Escenario 3: Refresh token expirado

Dado que mi refresh token tiene más de 30 días de antigüedad, Cuando intento renovar mi JWT, Entonces el sistema retorna error 401 "Refresh token expirado" y me redirige al login.

Escenario 4: Usuario desactivado durante sesión activa

Dado que mi usuario fue desactivado mientras tengo una sesión activa, Cuando intento renovar mi JWT, Entonces el sistema retorna error 403 "Usuario desactivado" y elimina el refresh token.

Escenario 5: Renovación automática transparente

Dado que estoy trabajando en el sistema y mi JWT expira, Cuando realizo una acción que requiere autenticación, Entonces el sistema renueva el JWT automáticamente sin que yo note ninguna interrupción.


Reglas de Negocio

  • RN-1: JWT expira en 8 horas, refresh token expira en 30 días.
  • RN-2: Al renovar JWT, se genera NUEVO refresh token (rotación de tokens).
  • RN-3: Refresh token anterior se marca como used=true y no puede reusarse.
  • RN-4: Un usuario puede tener múltiples refresh tokens activos (múltiples dispositivos).
  • RN-5: Al hacer logout, solo se elimina el refresh token del dispositivo actual.
  • RN-6: Al cambiar contraseña, se revocan TODOS los refresh tokens del usuario.
  • RN-7: Refresh tokens almacenan: user_agent, ip_address, last_used_at para auditoría.

Tareas Técnicas (Checklist de Implementación)

Backend

  • Crear endpoint: POST /api/v1/auth/refresh
  • Implementar método: AuthService.refreshToken(refreshToken)
  • Implementar método: AuthService.validateRefreshToken(token)
  • Implementar método: AuthService.rotateRefreshToken(oldToken)
  • Crear tabla: auth.sessions (refresh_token, user_id, tenant_id, user_agent, ip, expires_at, used, revoked)
  • Crear DTO: RefreshTokenDto con validación @IsJWT
  • Implementar lógica de rotación de tokens (invalidar anterior, generar nuevo)
  • Agregar unit tests para refreshToken (6 test cases)
  • Agregar integration tests para POST /auth/refresh (5 test cases)
  • Documentar endpoint en Swagger
  • Implementar job de limpieza de tokens expirados (cron daily)

Frontend

  • Crear API client: authApi.refresh(refreshToken)
  • Implementar Axios interceptor para renovación automática en 401
  • Implementar lógica: detectar JWT expirado antes de request (jwt-decode)
  • Almacenar refresh token en localStorage/sessionStorage (según "Recordar sesión")
  • Implementar retry logic: si refresh falla, redirect a login
  • Agregar tests para Axios interceptor (4 test cases)
  • Implementar UI feedback durante renovación (opcional: loading bar)
  • Manejar race conditions (múltiples requests simultáneos con JWT expirado)

Database

  • Crear tabla: auth.sessions con campos mencionados
  • Crear índice: idx_sessions_refresh_token (UNIQUE)
  • Crear índice: idx_sessions_user_id para listar sesiones activas por usuario
  • Agregar trigger: marcar last_used_at al renovar token
  • Validar foreign keys: user_id → auth.users, tenant_id → auth.tenants

Mockups / Wireframes

No hay UI visible para esta funcionalidad (proceso transparente al usuario).

Feedback visual opcional:

  • Durante renovación: Pequeño loading indicator en la esquina superior (opcional)
  • Fallo de renovación: Modal "Su sesión ha expirado. Por favor inicie sesión nuevamente" con botón "Ir al Login"

Developer Tools (para debugging):

  • En console.log: "JWT expired, refreshing token..."
  • En console.log: "Token refreshed successfully"
  • En console.log: "Refresh failed, redirecting to login"

Casos de Prueba (Test Scenarios)

Pruebas Funcionales

  1. TC-001: Refresh exitoso

    • Input: refresh token válido y no expirado
    • Expected: Nuevo JWT + nuevo refresh token, anterior marcado como used=true
  2. TC-002: Refresh token inválido

    • Input: refresh token que no existe en DB
    • Expected: Error 401 "Invalid refresh token"
  3. TC-003: Refresh token expirado

    • Input: refresh token con expires_at < now()
    • Expected: Error 401 "Refresh token expired"
  4. TC-004: Refresh token ya usado

    • Input: refresh token con used=true
    • Expected: Error 401 "Refresh token already used"
  5. TC-005: Usuario desactivado

    • Input: refresh token válido pero user.status='inactive'
    • Expected: Error 403 "User inactive", token eliminado de DB
  6. TC-006: Rotación de tokens

    • Input: refresh exitoso
    • Expected: Anterior token marcado used=true, nuevo token creado en sessions
  7. TC-007: Múltiples dispositivos

    • Input: usuario con 3 refresh tokens activos (desktop, mobile, tablet)
    • Expected: Renovar en un dispositivo no afecta tokens de otros dispositivos

Pruebas No Funcionales

  1. Performance: Response time < 100ms (solo validación de token, sin bcrypt)
  2. Seguridad:
    • Refresh token generado con 256 bits de entropía (crypto.randomBytes)
    • Stored securely (no se expone en logs)
    • HTTPS required en producción
  3. Concurrencia:
    • Manejar race condition: si 2 requests simultáneos intentan refresh, solo 1 debe tener éxito
    • Implementar lock/transaction para evitar doble rotación

Dependencias

  • User Stories bloqueantes: US-MGN-001-001-001 (Login con Email y Contraseña)
  • Módulos requeridos: Ninguno
  • Datos maestros necesarios: Usuario autenticado con refresh token existente

Notas de Implementación

  • Refresh token debe generarse con crypto.randomBytes(64).toString('hex') (Node.js)
  • Implementar interceptor en Axios para renovación automática:
    axios.interceptors.response.use(
      response => response,
      async error => {
        if (error.response?.status === 401 && !error.config._retry) {
          error.config._retry = true;
          const newToken = await authService.refresh();
          error.config.headers.Authorization = `Bearer ${newToken}`;
          return axios(error.config);
        }
        return Promise.reject(error);
      }
    );
    
  • Considerar implementar "sliding window" para refresh token (extender vida si se usa frecuentemente)
  • Implementar endpoint GET /auth/sessions para listar sesiones activas del usuario
  • Implementar endpoint DELETE /auth/sessions/:id para logout de dispositivo específico

Estimación Detallada

Tarea Estimación
Backend Development 2 horas
Frontend Development 2 horas
Testing (Unit + Integration) 1 hora
Code Review 0.5 horas
TOTAL 5.5 horas

Equivalente: 3 Story Points


Definition of Done (DoD)

  • Código backend implementado según ET-BACKEND-MGN-001-001
  • Código frontend implementado según ET-FRONTEND-MGN-001-001
  • Unit tests escritos y pasando (cobertura > 80%)
  • Integration tests escritos y pasando
  • Axios interceptor funcionando correctamente
  • Code review completado y aprobado
  • Documentación Swagger actualizada
  • Job de limpieza de tokens expirados implementado
  • Merge a branch develop completado
  • QA manual: probar expiración de JWT y renovación automática

Referencias