‫Instructor: Y ahora, finalmente creemos la última parte de
‫la funcionalidad de restablecimiento de
‫contraseña, donde realmente establecemos la nueva contraseña para el usuario.
‫Y así, como antes, comencemos por definir los
‫pasos que vamos a tomar para este flujo de resetPassword.
‫Entonces, en primer lugar, obtenga el usuario según el token.
‫Luego, como segundo paso, estableceremos la nueva contraseña, pero
‫solo si el token no ha expirado y
‫hay un usuario.
‫Entonces, en ese caso, configure la nueva contraseña.
‫Luego, después de eso, necesitamos actualizar la propiedad
‫modifiedPasswordAt para el usuario actual, y finalmente, como
‫es habitual en
‫esta funcionalidad, es iniciar la sesión del
‫usuario.
‫Básicamente, envíe el JSON Web Token al cliente.
‫Bien, mucho trabajo por hacer, así que comencemos.
‫Y entonces, recuerde del último video, que el token de
‫restablecimiento que se envía realmente en la URL
‫es este token no cifrado aquí.
‫Entonces, en realidad este.
‫Pero el que tenemos en la base
‫de datos es el cifrado.
‫Así que hablamos de eso antes, y lo que ahora
‫tenemos que hacer es básicamente encriptar el token
‫original nuevamente, para luego
‫compararlo con el que está almacenado, por lo que
‫el encriptado en la base de datos.
‫Entonces, en realidad hicimos algo similar antes
‫con la contraseña, pero con la contraseña, no
‫pudimos compararla tan fácilmente como pudimos con esta, nuevamente
‫porque para la contraseña
‫usamos el algoritmo bcrypt bastante complejo, que en este
‫caso, nosotros no lo hicimos.
‫Entonces, aquí es muy sencillo.
‫Todo lo que tenemos que hacer de nuevo es
‫cifrar el token y compararlo con el cifrado en la base de datos.
‫Entonces, digamos hashedToken, por lo que ahora también
‫necesitaremos el paquete criptográfico aquí.
‫Const crypto y luego require ('crypto').
‫Ahora a la derecha.
‫Entonces volvamos, y entonces usamos
‫criptografía. createHash.
‫Recuerde, luego el nombre del algoritmo
‫nuevamente, sha256, luego. actualizar, básicamente para el lugar, para la
‫cadena que queremos hash.
‫Y entonces, ese es, recuerde en req. params.
‫Entonces usamos este durante mucho tiempo. simbólico.
‫Y de nuevo, es un
‫parámetro, porque lo especificamos aquí en la URL, así.
‫Entonces, ahora es un parámetro llamado token.
‫Y entonces, por supuesto, aquí es req. params. simbólico.
‫Y luego, finalmente, también necesitamos decir digerir
‫y convertirlo a hexadecimal.
‫Ahora, esto es básicamente lo mismo
‫que teníamos antes, donde encriptamos el original, y así
‫podríamos refactorizar esto en su propia función, pero mantengamos
‫las cosas simples aquí.
‫Entonces, ahora obtengamos al usuario en función de este token.
‫Porque eso es, en realidad, lo
‫único que sabemos sobre el usuario en este momento.
‫No tenemos correo electrónico, no tenemos nada, por lo que este
‫token es lo único que puede identificar al usuario.
‫Y ahora podemos, básicamente, consultar la base de
‫datos para este token.
‫Y luego encontrará al usuario que tiene este token.
‫Entonces, espere, como ya sabemos, y luego Usuario. Encuentra uno.
‫Entonces, esa propiedad se llama
‫passwordResetToken y estamos buscando el hashToken.
‫Y ahora, por supuesto, debemos declararlo como asíncrono y
‫prepararlo en catchAsync.
‫Salvarlo, eso debería solucionar este error, y
‫de hecho lo hace.
‫Entonces, esto encontrará al usuario que tiene el token que
‫enviará a través de la URL.
‫Pero, en este momento, no
‫estamos tomando en consideración la fecha de vencimiento del token.
‫¿Y cómo podríamos hacer eso?
‫Bueno, básicamente, lo que queremos es comprobar
‫si la propiedad passwordResetExpires es mayor que
‫en este momento.
‫Porque si la fecha de vencimiento es mayor que
‫ahora, significa que está en el futuro, lo que a su vez
‫significa que aún no ha vencido.
‫Entonces, esa es una manera
‫muy fácil en la que realmente podemos hacer esto bien con esta consulta.
‫Entonces, passwordResetExpires, que es donde se
‫almacena esa fecha, y ahora todo
‫lo que necesitamos
‫para verificar si es realmente mayor que ahora.
‫Y entonces ya sabemos cómo hacer eso con MongoDB, ¿verdad?
‫Entonces, nuevo objeto y luego el operador mayor y luego con lo que
‫queremos compararlo es Fecha. ahora, y esta será
‫una marca de tiempo de ahora, pero
‫detrás de escena, MongoDB convertirá todo a lo mismo
‫y, por lo tanto, podrá compararlos
‫con precisión.
‫Y así, con esto podemos, al mismo
‫tiempo, encontrar el usuario del token y también verificar si el
‫token aún no ha caducado.
‫Que bien.
‫Entonces, a continuación queremos, por supuesto, enviar un
‫error si no hay ningún usuario, o básicamente, si el token ha expirado.
‫Pero eso es, en
‫este caso, lo mismo, porque si el token ha expirado,
‫entonces simplemente no devolverá ningún usuario.
‫Entonces, todo lo que tenemos que hacer es decir,
‫si no hay usuario, bueno, como siempre, regrese a continuación, eso no es mext.
‫Entonces, nuevo AppError, y digamos que
‫el token no es válido o ha caducado.
‫Y luego 400, tan mala solicitud.
‫Entonces, si no hay ningún error, y
‫si no se llama
‫a next, entonces establezcamos la contraseña.
‫Entonces, ya tenemos el usuario y ahora es muy
‫simple: usuario. contraseña es igual a req. cuerpo. contraseña.
‫Y eso es porque, por supuesto, enviaremos la
‫contraseña y también la contraseñaConfirmar a través del cuerpo.
‫Así que
‫dupliquemos eso y también passwordConfirm.
‫Y luego también, básicamente eliminemos el token de reinicio
‫y el caducado.
‫Entonces passwordResetToken, así como lo hicimos antes, lo configuramos como
‫indefinido, y ahora usuario. la contraseña expira es igual
‫a indefinida.
‫Está bien.
‫Y nuevamente, por supuesto, ahora necesitamos guardarlo,
‫porque esto solo modifica el documento, en realidad
‫no se actualiza.
‫Entonces realmente no lo salva.
‫Entonces, espera al usuario. ahorrar.
‫Y en este caso, en realidad no
‫tenemos que apagar los validadores, porque de hecho queremos validar.
‫Por ejemplo, queremos que el validador
‫confirme si la contraseña es igual a passwordConfirm.
‫Y ese validador automáticamente hace todo
‫ese trabajo por nosotros.
‫Luego, el tercer paso, lo que haremos realmente
‫al final, y lo que haremos a continuación
‫es básicamente encerrar al usuario.
‫En otras palabras, envíe el JSON Web Token.
‫Y obtengamos ese código de aquí, así que este.
‫Y nuevamente, ya estamos haciendo
‫esto aquí en tres lugares diferentes.
‫Así que aquí en el inicio de sesión, también en el registro,
‫y ahora por tercera vez, aquí abajo.
‫Y así, en algún momento en el futuro, lo refactorizaremos en
‫su propia función.
‫Pero por ahora, estamos bien así.
‫Y entonces, vayamos ahora a probar esto.
‫Entonces, este token de reinicio
‫que teníamos antes ya venció, por lo
‫que debemos solicitar uno nuevo, básicamente.
‫Así que vayamos a Postman y optemos por nuestra ruta para olvidar la contraseña.
‫Reduzcamos el desorden aquí y
‫eliminemos todas estas pestañas abiertas que ya no
‫necesitamos.
‫En realidad, aquí vamos a necesitar esta
‫prueba para restablecer la contraseña,
‫porque recuerde, que esta en realidad recupera un JSON
‫Web Token, por lo que queremos guardarlo en la variable de
‫entorno, al igual
‫que hicimos con todos los demás.
‫Así que lo estoy haciendo ahora, solo para no olvidarlo.
‫Muy bien, de todos modos, comencemos, básicamente,
‫olvidando la contraseña.
‫Entonces, enviar esa solicitud, que nuevamente, toma algunos
‫tiempos debido al envío del
‫correo electrónico, pero aquí vamos, y ahora vayamos a nuestro correo
‫electrónico, y eso llegó hace unos segundos.
‫Entonces, es esta, por supuesto, esta ficha.
‫Así que vamos a agarrarlo, copiarlo y ahora de vuelta a Postman, lo
‫usamos en Restablecer contraseña, como la URL.
‫Vale, ¿tiene sentido?
‫De nuevo, enviamos ese token directamente en la URL.
‫Entonces aquí, especifiquemos el
‫cuerpo, porque ahora, necesitamos especificar nuestra nueva contraseña.
‫Entonces contraseña y digamos newpass.
‫Y luego...
‫Y aquí llamémoslo de
‫otra manera, porque por ahora, realmente quiero ver un error.
‫Y, por supuesto, esto se llama passwordConfirm.
‫Entonces, veamos qué sucede cuando intentamos hacer esto.
‫Esperemos y obtenemos que la contraseña es más corta
‫que la longitud mínima permitida.
‫Bien, cambiemos eso, 123, y aquí digamos 1234.
‫Por eso quiero que sean diferentes.
‫Pero ve que la validación aquí funcionó bien,
‫incluso al actualizar la contraseña con guardar.
‫Entonces, ¡y ahora obtenemos las contraseñas no son las mismas!
‫De nuevo, eso es un error de validación.
‫Y recuerde, en realidad, que esta es la
‫razón por la que necesitamos usar guardar y no actualizar.
‫Entonces, antes, para actualizar
‫los recorridos, solíamos usar findOneAndUpdate, pero ahora, para todo lo
‫relacionado con las contraseñas y el usuario, siempre
‫usamos guardar, porque siempre queremos ejecutar todos los
‫validadores y, sobre todo, las funciones
‫de guardar middleware.
‫Entonces, por ejemplo, aquellos en los que las contraseñas están encriptadas.
‫Así que terminemos ahora.
‫Oh, en realidad no lo corrigí, lo siento.
‫Y ahora debería funcionar.
‫Y, de hecho, logramos el éxito y obtenemos una nueva ficha.
‫Genial, veamos si este token es realmente válido.
‫Entonces, si podemos, obtenga todos los recorridos con este nuevo token.
‫Y aquí vamos.
‫Entonces, nuestro nuevo token realmente funciona, y ahora para este
‫usuario, para hello @ jonas, estas
‫dos propiedades deberían desaparecer.
‫Entonces, la contraseña caduca y el token debería desaparecer,
‫ya que, bueno, eso es lo que hicimos en nuestro código.
‫Y entonces, sí, ya no están aquí.
‫Ahora todo lo que tenemos que hacer es este paso que falta
‫aquí, que es actualizar la propiedad passwordAt para
‫este usuario actual.
‫Pero eso no
‫debería ser demasiado difícil, así que volvamos rápidamente al
‫modelo de usuario, que es donde lo haremos usando middleware.
‫Y realmente juntemos todo el middleware
‫aquí en la parte superior.
‫Entonces, userSchema. pre y de
‫nuevo dot save, y luego una función con next.
‫Nuevamente, esta función se ejecutará
‫justo antes de que se guarde un nuevo documento.
‫Por lo tanto,
‫es el lugar perfecto para especificar esta propiedad.
‫Y, por supuesto, podría haberlo hecho en un controlador
‫justo al lado de este código, por ejemplo.
‫Pero realmente quiero que esto suceda, algo así, automáticamente.
‫Entonces, algo detrás de escena.
‫Porque más adelante, tendremos otro lugar
‫donde actualizaremos la contraseña y luego nos aseguraremos de incluir
‫el mismo código allí.
‫Y así, de nuevo, sucede, algo
‫así, detrás de
‫escena, sin que tengamos que preocuparnos por eso en absoluto.
‫Ahora, ¿cuándo exactamente queremos
‫establecer la propiedad passwordChangedAt en este momento?
‫Bueno, solo lo queremos cuando realmente modificamos la propiedad
‫de la contraseña.
‫Y no estoy seguro si usamos este truco
‫antes, pero de todos modos, usémoslo ahora.
‫Entonces, si no lo hemos modificado, entonces si no este. isModified, así que así, y
‫luego el
‫nombre de la propiedad, por lo que contraseña.
‫Entonces, en ese caso, regrese de inmediato y
‫ejecute el siguiente middleware.
‫De acuerdo, no así, sino así.
‫Entonces, nuevamente, si no modificamos la propiedad
‫de
‫la contraseña, bueno, por supuesto, no manipule el passwordChangedAt.
‫Pero, ¿qué pasa con la creación de un nuevo documento?
‫Bueno, cuando creamos un nuevo
‫documento, modificamos la
‫contraseña y luego configuramos la propiedad passwordChangedAt, ¿verdad?
‫Bueno, en la implementación actual lo haríamos.
‫Pero hay algo más que podemos usar aquí.
‫Entonces, básicamente, queremos salir de esta función de
‫middleware de inmediato, si la contraseña no se ha modificado
‫o si el documento es nuevo, y entonces podemos usar
‫la propiedad isNew.
‫Y nuevamente, esta es una de estas cosas muy buenas que
‫se aprenden leyendo su documentación.
‫Entonces, no puedo enfatizar lo suficiente lo importante que es leer
‫realmente la documentación cuando necesitas algo que no puedes
‫encontrar en ningún lado.
‫Porque realmente hay tantas
‫cosas ahí que es completamente imposible enseñar en un curso.
‫De todos modos, si el código pasa esta
‫verificación aquí, bueno, simplemente digamos
‫esto. passwordChangedAt = Fecha. ahora.
‫Y luego, llamamos a continuación.
‫Ahora, en teoría, esto debería funcionar bien, pero
‫en realidad, en
‫la práctica, a veces ocurre un pequeño problema.
‫Y ese problema es que, a veces, guardar en la base de
‫datos es un poco más lento que emitir el JSON Web Token, por lo
‫que la marca de tiempo de la contraseña cambiada a
‫veces se establece un poco después
‫de que se ha creado el JSON Web Token.
‫Y eso hará que el usuario
‫no pueda iniciar sesión con el nuevo token.
‫Porque, recuerde, la única razón por la que esta marca de
‫tiempo aquí realmente existe es para que podamos
‫compararla con la marca de tiempo del JSON Web Token, ¿verdad?
‫Entonces, solo para recordar, es, bueno,
‫aquí mismo, donde verificamos si el usuario ha cambiado la contraseña
‫después de que se emitió el token.
‫Y así, aquí abajo, donde creamos este nuevo token
‫en el restablecimiento de contraseña.
‫Así que aquí mismo, recuerde, creamos este nuevo token
‫y, de nuevo, a veces sucede que este token se crea
‫un poco antes de que se haya creado realmente la marca
‫de tiempo de la contraseña modificada.
‫Entonces, solo necesitamos arreglar eso restando un segundo.
‫Entonces, básicamente, mil milisegundos.
‫Y entonces pondrá la contraseñaChangedA en un segundo en el pasado,
‫está bien, lo que, por supuesto, no
‫será 100% exacto, pero eso no es un problema en
‫absoluto, porque un segundo aquí no hace ninguna diferencia.
‫Es un truco pequeño, pero de nuevo, no hay problema.
‫Por lo tanto, si coloca esta contraseñaChanged un segundo en el pasado, se
‫asegurará de que el token siempre se cree después
‫de que se haya cambiado la contraseña.
‫Entonces, esto funciona ahora,
‫pero como siempre, también probémoslo rápidamente.
‫Bien, volvamos a Postman.
‫Hagamos un nuevo restablecimiento de contraseña, o en realidad,
‫eso no es lo que quería
‫en absoluto, pero es genial ver que
‫el código realmente funciona.
‫Entonces, el token no es válido o ha caducado, y eso
‫se debe a que, bueno, han pasado
‫10 minutos desde que realmente creé ese token.
‫Y creo que todavía no lo habíamos
‫probado, por lo que es genial que ahora lo haya hecho accidentalmente.
‫De nuevo, esto viene, en caso de que se esté preguntando qué diablos
‫sucedió, así que, por
‫supuesto, ese es este mensaje de error aquí.
‫Y entonces significa que no encontró ningún usuario que
‫tenga este token o que tenga un token que tenga más de
‫10 minutos en el pasado.
‫Y entonces, de hecho, lo que quería hacer era olvidar la contraseña.
‫Entonces, esperemos.
‫Entonces 8. 6 segundos,
‫pero eso podría deberse a mi conexión a Internet.
‫Entonces, si ejecuta esto en
‫un servidor, probablemente será mucho más rápido.
‫Así que tomemos eso aquí, volvamos a Postman, y ahora
‫restablecemos la contraseña, nuevamente con esta contraseña, realmente no importa si
‫es la misma que la anterior.
‫Y ahora tenemos nuestro éxito aquí.
‫Y ahora volvamos a Compass.
‫Recarguemos, y de hecho, obtenemos el passwordChangedAt.
‫Y eso es realmente ahora.
‫Entonces, si ahora intentamos usar este token, por ejemplo, para acceder
‫a esta ruta protegida, entonces eso debería
‫funcionar debido a esa pequeña
‫etiqueta de un segundo que hicimos.
‫Entonces, así fue y así, así, de hecho, ahora terminamos
‫nuestra Funcionalidad de restablecimiento de contraseña.
‫Así que eso fue bastante código, pero por supuesto,
‫vale la pena.
‫Por lo tanto, siempre debe ofrecer
‫esta funcionalidad en su aplicación web, porque
‫de lo contrario, un usuario que olvida su contraseña está completamente jodido,
‫ya no puede usar su aplicación, por
‫lo que, por supuesto, esa es una práctica terrible.
‫De todos modos, este tipo
‫de finaliza, ya, la parte de autenticación y autorización de esta sección.
‫Nuevamente, está bastante completo
‫y me divertí mucho implementando esto.
‫Entonces, esta parte para mí es donde las aplicaciones
‫web realmente comienzan a cobrar vida.
‫Sé que no es realmente visible en este punto,
‫con todos estos, solo tokens y tokens de copia
‫y pegarlos en otro lugar.
‫Esa no es la idea habitual que tenemos de iniciar sesión,
‫lo sé, pero, por supuesto, nuevamente, un poco más
‫tarde, cuando finalmente comencemos a construir el sitio web
‫dinámico, entonces, por
‫supuesto, seguiremos usando esta autenticación que acabamos
‫de construir y luego también se volverá visual en ese sitio web dinámico.
‫A continuación, implementaremos la funcionalidad para actualizar el
‫usuario y también eliminarlo, y
‫después de eso, también hablaremos de seguridad.
‫Eso es lo que nos espera en el resto de la
‫sección, así que asegúrese de no perderse eso.