Files
SIG-CM2.0/docs/smoke-test-udt-002.md

4.5 KiB

Smoke Test — UDT-002: Logout + Refresh Token

Branch: feature/UDT-002
Fecha: 2026-04-14
Prerequisito: backend corriendo en http://localhost:5212, BD SIGCM2 con migración V002 aplicada.


Escenario 1 — Login y persistencia de tokens

  • Abrir la app en http://localhost:5173
  • Ingresar con credenciales válidas (admin / password)
  • Verificar que el login redirige al home
  • Abrir DevTools → Application → Local Storage → auth-storage
  • Confirmar que el objeto contiene: accessToken, refreshToken, expiresAt, user
  • Verificar que expiresAt es aproximadamente Date.now() + 3600000 (1 hora)

Escenario 2 — Refresh transparente en 401

Opción A (esperar expiración natural — requiere token con TTL corto):

  • Modificar Jwt:AccessTokenMinutes a 1 en appsettings.Development.json y reiniciar el backend
  • Hacer login
  • Esperar 1 minuto para que el access token expire
  • Realizar cualquier request autenticado (ej: navegar a una sección que llame a la API)
  • Verificar que el request se completa sin error visible para el usuario
  • Verificar en DevTools → Network que hubo una llamada a POST /api/v1/auth/refresh seguida del request original reenviado con un nuevo Bearer

Opción B (manipulación manual del token):

  • Después del login, abrir DevTools → Application → Local Storage → auth-storage
  • Editar el JSON y reemplazar accessToken con un valor inválido (ej: "expired")
  • Realizar cualquier request autenticado
  • El interceptor de axiosClient recibe 401, llama a /refresh con el refreshToken real
  • El request original se reintenta automáticamente con el nuevo accessToken
  • El usuario no ve ningún error

Escenario 3 — Refresh de 3 requests paralelos (singleton promise)

  • Con el access token vencido (opción B del escenario 2)
  • Abrir una página que dispare múltiples llamadas API simultáneas
  • Verificar en DevTools → Network que hay exactamente 1 llamada a POST /api/v1/auth/refresh
  • Verificar que todos los requests subsiguientes retornan con éxito

Escenario 4 — Logout

  • Con sesión activa, hacer click en el botón de logout
  • Verificar que redirige a /login
  • Verificar en DevTools → Network que se llamó a POST /api/v1/auth/logout
  • Verificar en Local Storage que auth-storage tiene user: null, accessToken: null, refreshToken: null
  • Intentar navegar a una ruta protegida — debería redirigir a login

Escenario 5 — Reuso de refresh token después del logout (reuse detection)

  • Hacer login y copiar el valor de refreshToken del Local Storage
  • Hacer logout
  • Intentar llamar manualmente al endpoint de refresh con el token anterior:
    curl -X POST http://localhost:5212/api/v1/auth/refresh \
      -H "Content-Type: application/json" \
      -d '{"accessToken": "<access-anterior>", "refreshToken": "<refresh-anterior>"}'
    
  • Verificar que el backend responde 401 con { "error": "invalid_token" }
  • Verificar en la BD que todos los tokens de la familia fueron revocados:
    SELECT * FROM dbo.RefreshToken WHERE RevokedAt IS NOT NULL ORDER BY Id DESC;
    

Escenario 6 — Refresh token expirado (7 días)

  • Modificar ExpiresAt de un token en la BD SIGCM2_Test a una fecha pasada
  • Intentar refresh con ese token — debería responder 401
  • Verificar que el frontend redirige a /login y limpia el Local Storage

Escenario 7 — Refresh con access token de otro usuario (mismatch)

  • Crear dos usuarios en la BD (o usar admin + otro)
  • Hacer login con usuario A, guardar el accessToken
  • Hacer login con usuario B, guardar el refreshToken
  • Intentar refresh con accessToken de A + refreshToken de B:
    curl -X POST http://localhost:5212/api/v1/auth/refresh \
      -H "Content-Type: application/json" \
      -d '{"accessToken": "<access-usuario-A>", "refreshToken": "<refresh-usuario-B>"}'
    
  • Verificar que el backend responde 401

Notas de verificación

Check Comando
Tokens en BD SELECT Id, UsuarioId, FamilyId, IssuedAt, ExpiresAt, RevokedAt FROM dbo.RefreshToken ORDER BY Id DESC
Familias revocadas SELECT FamilyId, COUNT(*) as Total, SUM(CASE WHEN RevokedAt IS NOT NULL THEN 1 ELSE 0 END) as Revoked FROM dbo.RefreshToken GROUP BY FamilyId
Usuario activo SELECT Id, Username, Activo FROM dbo.Usuario