Saltar a contenido

Documento histórico

Este documento se conserva solo para trazabilidad. Consulte el índice maestro para la fuente vigente.

Documento histórico — no usar como fuente de decisiones actuales

Reemplazado por la versión canónica del 2026-07-11. Conservar solo como trazabilidad histórica.


CU-002: Iniciar sesión / Recuperar contraseña

Caso de uso detallado para login y recuperación de contraseña.


Metadatos

Campo Valor
ID CU-002
Nombre Iniciar sesión / Recuperar contraseña
Actor primario Usuario Final
Actores secundarios Sistema de Notificaciones (SMS OTP)
Módulo Autenticación y registro
Prioridad Alta (MVP)
Frecuencia esperada Muy alta (varias veces al día por usuario)
Trazabilidad SRS RF-004, RF-005, RF-002

Parte A: Iniciar sesión

Precondiciones

  • El usuario tiene una cuenta registrada.
  • El usuario conoce su correo y contraseña.

Postcondiciones

  • Se inicia sesión y se redirige al dashboard.
  • Se emite un token de sesión (JWT) con vigencia 30 días (refresh token).

Flujo principal

  1. El usuario abre la app y ve la pantalla Welcome.
  2. El usuario toca Iniciar Sesión.
  3. El sistema muestra la pantalla Login con campos: email, contraseña.
  4. El usuario ingresa su email y contraseña.
  5. El usuario toca Entrar.
  6. El sistema valida formato de email.
  7. El sistema valida que el email existe en la BD → si no, error genérico.
  8. El sistema valida la contraseña con el hash almacenado (bcrypt/argon2) → si no coincide, error genérico.
  9. El sistema genera un access_token (válido 15 min) y un refresh_token (válido 30 días).
  10. El sistema almacena el refresh_token en almacenamiento seguro del dispositivo.
  11. El sistema navega al Dashboard.

Flujos alternativos

FA-1: Credenciales inválidas

  • En el paso 7 u 8, si las credenciales no son válidas:
  • El sistema muestra "Correo o contraseña incorrectos." (genérico, no revela si el email existe).
  • Incrementa el contador de intentos fallidos en Redis (clave login_attempts:{ip}:{email}).
  • Si el contador llega a 5 en 1 minuto, devuelve RATE_LIMIT_EXCEEDED y bloquea por 15 minutos.

FA-2: Cuenta bloqueada

  • Si el usuario tiene is_active = false:
  • Muestra "Tu cuenta está bloqueada. Contacta a soporte."

FA-3: Cuenta no verificada

  • Si el usuario tiene phone_verified = false:
  • Muestra "Verifica tu teléfono para continuar" y CTA a VerificationCode.

FA-4: Cuenta con KYC vencido

  • Si el KYC del usuario está vencido:
  • Permite el login, pero muestra un banner en el dashboard: "Tu verificación KYC venció. Renuévala para poder despachar."

Excepciones

Excepción Causa Manejo
INVALID_CREDENTIALS Email o contraseña incorrectos Mensaje genérico
RATE_LIMIT_EXCEEDED 5+ intentos en 1 min Bloqueo temporal 15 min
ACCOUNT_BLOCKED Cuenta con is_active = false Mensaje + soporte
ACCOUNT_NOT_VERIFIED phone_verified = false CTA a verificación

Parte B: Recuperar contraseña

Precondiciones

  • El usuario tiene una cuenta registrada con phone_verified = true.

Postcondiciones

  • Se actualiza la contraseña del usuario.
  • Se invalidan todas las sesiones activas del usuario en otros dispositivos.

Flujo principal

  1. Desde la pantalla Login, el usuario toca ¿Olvidaste tu contraseña?.
  2. El sistema muestra la pantalla ForgotPassword con un campo de email.
  3. El usuario ingresa su email.
  4. El usuario toca Enviar código.
  5. El sistema valida formato de email.
  6. Por seguridad, el sistema siempre muestra el mismo mensaje: "Si el email existe, te enviaremos un código de verificación al teléfono registrado."
  7. El sistema busca al usuario por email.
  8. Si existe, genera un OTP de 6 dígitos, lo guarda en Redis (TTL 5 min, clave otp:reset:{user_id}) y lo envía al +1.
  9. El sistema navega a la pantalla VerificationCode (reset) con el email del usuario.
  10. El usuario recibe el SMS e ingresa los 6 dígitos.
  11. El sistema valida el OTP.
  12. Si es correcto, navega a la pantalla ResetPassword.
  13. El usuario ingresa la nueva contraseña dos veces.
  14. El sistema valida requisitos de contraseña y que coincidan.
  15. El usuario toca Cambiar contraseña.
  16. El sistema hashea la nueva contraseña y la actualiza.
  17. El sistema invalida todos los refresh_token anteriores del usuario (registra el cambio en audit_logs).
  18. El sistema navega a la pantalla Success con mensaje "Contraseña actualizada".
  19. El usuario toca Ir a Login.
  20. El sistema navega a Login (con la sesión del back stack aún presente, sin pop).

Flujos alternativos

FA-5: Email no registrado

  • En el paso 7, si el email no existe:
  • Se muestra el mismo mensaje genérico del paso 6 (por seguridad).

FA-6: OTP incorrecto

  • Igual que en CU-001 (3 intentos, luego reenvío).

FA-7: Nueva contraseña no cumple requisitos

  • En el paso 14, si la contraseña no cumple:
  • Muestra los requisitos en rojo y deshabilita el botón.

FA-8: Contraseña igual a la anterior

  • En el paso 16, si la nueva contraseña coincide con la anterior:
  • Muestra "La nueva contraseña no puede ser igual a la anterior."

Excepciones

Excepción Causa Manejo
OTP_INVALID Código incorrecto Mensaje con intentos restantes
OTP_EXPIRED 5 min transcurridos Habilitar reenvío
OTP_MAX_ATTEMPTS 3 intentos Invalidar OTP
PASSWORD_REUSED Igual a anterior Mensaje específico
WEAK_PASSWORD No cumple requisitos Lista de requisitos en rojo

Reglas de negocio aplicables

  • RN-3 (OTP): 5 min de vigencia, 3 intentos máximo.
  • RN-4 (Rate limit): login 5 intentos/min. Ver requisitos.md.
  • RN-5 (Genérico en errores): nunca revelar si un email existe en flujos de recuperación.

Criterios de aceptación

  • Login exitoso en < 2 segundos en condiciones normales.
  • Mensaje de error genérico en credenciales inválidas (no filtra existencia).
  • Rate limit efectivo: tras 5 intentos en 1 min, bloqueo de 15 min.
  • OTP de recuperación expira en 5 minutos.
  • Tras cambio de contraseña, sesiones anteriores se invalidan.
  • Nueva contraseña debe cumplir los mismos requisitos que el registro.

Diagrama de actividad

Login

flowchart TD
    A["Pantalla Welcome"] --> B["Toca 'Iniciar Sesión'"]
    B --> C["Pantalla Login"]
    C --> D["Ingresa email + contraseña"]
    D --> E["Toca 'Entrar'"]
    E --> F{"¿Credenciales válidas?"}
    F -- No --> G{"¿Rate limit?"}
    G -- No --> H["Error genérico + incrementa contador"]
    H --> C
    G -- Sí --> I["Bloqueo 15 min"]
    F -- Sí --> J["Genera tokens"]
    J --> K["Dashboard"]

Recuperación

flowchart TD
    A["Pantalla Login"] --> B["Toca '¿Olvidaste tu contraseña?'"]
    B --> C["Pantalla ForgotPassword"]
    C --> D["Ingresa email"]
    D --> E["Toca 'Enviar código'"]
    E --> F["Mensaje genérico (siempre)"]
    F --> G["Pantalla VerificationCode (reset)"]
    G --> H{"¿OTP correcto?"}
    H -- No --> I["Error / reintento"]
    I --> G
    H -- Sí --> J["Pantalla ResetPassword"]
    J --> K["Ingresa nueva contraseña 2x"]
    K --> L{"¿Válida?"}
    L -- No --> M["Muestra requisitos"]
    M --> J
    L -- Sí --> N["Actualiza hash"]
    N --> O["Invalida sesiones anteriores"]
    O --> P["Pantalla Success"]
    P --> Q["Pantalla Login"]

Pruebas asociadas

  • PT-010: Login exitoso
  • PT-011: Login con credenciales inválidas
  • PT-012: Login con cuenta bloqueada
  • PT-013: Rate limit tras 5 intentos
  • PT-014: Recuperación de contraseña completa
  • PT-015: OTP de recuperación expirado
  • PT-016: Nueva contraseña débil
  • PT-017: Nueva contraseña igual a la anterior
  • PT-018: Invalidación de sesiones tras cambio de contraseña