RF11: Usuario recupera contraseña
Descripción
Como usuario que olvidó su contraseña, quiero restablecerla mediante mi correo para recuperar el acceso a mi cuenta.
El flujo envía un enlace/código de un solo uso al correo registrado; al usarlo se fija una nueva contraseña y se revocan todas las sesiones (revoked_by = 'password_reset'), conforme al Control de Sesiones §5.
| Campo | Valor |
|---|---|
| Módulo | Auth Module |
| Actor | Usuario no autenticado (olvidó contraseña) |
| Endpoint | POST /auth/password/forgot + POST /auth/password/reset |
| Precondiciones | Existe una cuenta con contraseña asociada al correo |
| Prioridad | Alta (MVP) |
| Etapa | MVP |
| Requisitos relacionados | RF02, RF07 |
Reglas de negocio
- RN-11.1 — La respuesta a "olvidé mi contraseña" es siempre genérica ("Si el correo existe, enviamos instrucciones") para no revelar si el correo está registrado.
- RN-11.2 — El token de reseteo es de un solo uso, con expiración corta (p. ej. 30 min), y se almacena hasheado.
- RN-11.3 — Al completar el reseteo se revocan todas las sesiones del usuario y se notifica por email.
- RN-11.4 — Cuentas solo-social no tienen contraseña; el flujo informa que inicie con su proveedor.
- RN-11.5 — La nueva contraseña cumple la política del registro.
Validaciones de entrada
| Campo | Reglas | Mensaje de error |
|---|---|---|
email | Obligatorio. Formato válido. | "Ingresa un correo válido." |
resetToken | Obligatorio. Válido, no usado, no expirado. | "El enlace es inválido o expiró." |
newPassword | Obligatorio. Política del registro. | "La contraseña no cumple los requisitos." |
Criterios de aceptación
Escenario 1: Solicitud de recuperación
Dado que ingreso mi correo en "Olvidé mi contraseña", Cuando envío la solicitud, Entonces el sistema responde siempre con un mensaje genérico de éxito, Y si el correo existe y tiene contraseña, envía un enlace/código de un solo uso.
Escenario 2: Reseteo exitoso
Dado que abro el enlace válido y propongo una nueva contraseña válida,
Cuando confirmo el reseteo,
Entonces el sistema actualiza el hash Argon2id,
Y revoca todas mis sesiones,
Y responde 200 OK y me notifica por email,
Y puedo iniciar sesión con la nueva contraseña.
Escenario 3: Token inválido, usado o expirado (seguridad)
Dado que el token de reseteo es inválido, ya se usó o expiró,
Cuando intento restablecer,
Entonces el sistema responde 400 con "El enlace es inválido o expiró".
Escenario 4: Cuenta solo-social
Dado que mi cuenta fue creada solo con Google/Apple, Cuando solicito recuperar contraseña, Entonces el email me indica iniciar sesión con mi proveedor (no hay contraseña que restablecer).
Escenario 5: Abuso del endpoint (seguridad)
Dado que se solicitan muchos reseteos en poco tiempo,
Cuando se supera el umbral,
Entonces se aplica rate limiting (429) para mitigar enumeración y spam.
Criterios no funcionales
- El token nunca se almacena en claro (hash).
- Consultas parametrizadas; sin inyección SQL.
- Comunicación sobre TLS 1.2+.