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¶
- El usuario abre la app y ve la pantalla Welcome.
- El usuario toca Iniciar Sesión.
- El sistema muestra la pantalla Login con campos: email, contraseña.
- El usuario ingresa su email y contraseña.
- El usuario toca Entrar.
- El sistema valida formato de email.
- El sistema valida que el email existe en la BD → si no, error genérico.
- El sistema valida la contraseña con el hash almacenado (bcrypt/argon2) → si no coincide, error genérico.
- El sistema genera un
access_token(válido 15 min) y unrefresh_token(válido 30 días). - El sistema almacena el
refresh_tokenen almacenamiento seguro del dispositivo. - 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_EXCEEDEDy 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¶
- Desde la pantalla Login, el usuario toca ¿Olvidaste tu contraseña?.
- El sistema muestra la pantalla ForgotPassword con un campo de email.
- El usuario ingresa su email.
- El usuario toca Enviar código.
- El sistema valida formato de email.
- 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."
- El sistema busca al usuario por email.
- 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. - El sistema navega a la pantalla VerificationCode (reset) con el email del usuario.
- El usuario recibe el SMS e ingresa los 6 dígitos.
- El sistema valida el OTP.
- Si es correcto, navega a la pantalla ResetPassword.
- El usuario ingresa la nueva contraseña dos veces.
- El sistema valida requisitos de contraseña y que coincidan.
- El usuario toca Cambiar contraseña.
- El sistema hashea la nueva contraseña y la actualiza.
- El sistema invalida todos los
refresh_tokenanteriores del usuario (registra el cambio enaudit_logs). - El sistema navega a la pantalla Success con mensaje "Contraseña actualizada".
- El usuario toca Ir a Login.
- 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