Cambio Seguro de Contraseña con Token y Expiración

Clase 18 de 20Curso de Backend con Node.js: Autenticación con Passport.js y JWT

Contenido del curso

Passport y JSON Web Tokens

Resumen

Implementar un flujo completo de password recovery requiere más que enviar un correo: necesitas verificar tokens, comparar registros en base de datos, hashear contraseñas y limpiar credenciales temporales. Aquí se detalla paso a paso cómo construir el endpoint que cierra el ciclo de recuperación de contraseña de forma segura.

¿Cómo funciona el flujo completo de recuperación de contraseña?

El proceso comienza cuando el usuario ingresa su email en la interfaz de password recovery y se dispara el endpoint previamente construido [0:06]. El sistema valida que el correo exista en la base de datos, genera un token con expiración de quince minutos y lo envía por email. El front end muestra un mensaje confirmando el envío.

El correo contiene un enlace con la URL del front end que incluye una ruta especial como /recovery y el token como parámetro [0:37]. Cuando el usuario hace clic, llega a un formulario donde el front end lee el token de la URL y solicita la nueva contraseña. Al confirmar, se envían el token y la nueva contraseña al backend para su validación.

¿Cómo se construye el endpoint de cambio de contraseña?

En el auth router se crea una nueva ruta llamada change password que recibe por POST el token y el new_password en el body [1:17]. Es importante agregar una capa de validación de datos mediante un schema de Joi que verifique requisitos como longitud mínima, máxima y formato alfanumérico.

¿Qué validaciones se realizan en el servicio?

El método changePassword en el servicio ejecuta varias verificaciones envueltas en un bloque try-catch. Si algo falla, se lanza un error de tipo Boom unauthorized [2:23].

  • Verificación del token JWT: se usa jwt.verify() enviando el token y el secret con el que fue firmado. Si es válido, retorna el payload que contiene el identificador del usuario [2:40].
  • Búsqueda del usuario: con el ID extraído del payload (almacenado en el campo sub, que representa al subscriber o sujeto del token), se utiliza el método findOne para localizar al usuario. Si no existe, retorna un error automáticamente [3:15].
  • Doble verificación del token: se compara el token recibido con el recoveryToken almacenado en la base de datos. Si no coinciden, se rechaza la solicitud [3:42]. Este doble check previene la reutilización de tokens y garantiza que solo el token asignado en ese momento sea válido.

¿Cómo se hashea y actualiza la nueva contraseña?

Una vez superadas todas las validaciones, se genera un hash de la nueva contraseña usando bcrypt.hash() con el número de saltos configurado [4:45]. Luego se reutiliza el método update existente para asignar dos cambios al usuario:

  • El campo recoveryToken se establece en nulo, eliminando el token temporal.
  • El campo password se actualiza con el hash generado.

Finalmente, se retorna un mensaje indicando que la contraseña fue cambiada exitosamente [5:22].

¿Cómo se prueban los diferentes escenarios de seguridad?

Las pruebas en Insomnia demuestran la robustez del flujo con varios escenarios [5:35]:

  • Token expirado: al dejar pasar más de quince minutos, el sistema retorna unauthorized porque el token ya no es válido [6:22].
  • Token válido pero diferente: usar un token de acceso del login, aunque esté firmado con el mismo secret, también falla porque no coincide con el recoveryToken almacenado en base de datos [6:40].
  • Token corrupto: modificar incluso un carácter del token provoca rechazo inmediato [7:40].
  • Token válido dentro del rango: con un token recién generado y sin modificar, el cambio de contraseña se ejecuta correctamente [7:52].

Después del cambio, el login con la contraseña antigua falla y solo funciona con la nueva [8:05]. Además, el recoveryToken queda en nulo, impidiendo que alguien reutilice el mismo token para cambiar la contraseña nuevamente [8:20].

Este flujo integra el envío de correos con SMTP de Gmail, la asignación de tokens temporales con JWT y el hashing con bcrypt para lograr un proceso de recuperación de contraseña verdaderamente seguro. ¿Has implementado alguna validación adicional en tus proyectos? Comparte tu experiencia en los comentarios.