‫Jonas: Entonces, en
‫esta conferencia, implementaremos la funcionalidad de iniciar
‫la sesión de los usuarios en función de una contraseña y una
‫dirección de correo electrónico determinadas.
‫Y al igual que antes, el concepto de iniciar la sesión
‫de un usuario básicamente significa firmar un token web
‫JSON y enviarlo de vuelta al cliente.
‫Pero en este caso solo emitimos el token
‫en caso de que el usuario realmente exista
‫y que la contraseña sea correcta.
‫Entonces, comencemos a implementar eso.
‫Exportaciones. iniciar sesión es ...
‫solicitud, respuesta y siguiente.
‫Y lo primero que debemos hacer es
‫leer el correo electrónico y la contraseña del cuerpo.
‫Entonces, digamos que el correo electrónico constante es igual a req. cuerpo. Email.
‫Bien, y ahora eslint nos da esta advertencia, o este
‫error en realidad, y dice
‫que deberíamos usar la desestructuración de objetos, ¿de acuerdo?
‫Así que dejé que esto sucediera aquí
‫a propósito solo para poder mostrarles cómo podemos hacerlo
‫con la desestructuración de ES6.
‫Y creo que hablé de esto antes en el curso, y
‫también espero que ya esté familiarizado con
‫el concepto, pero de todos modos, dado
‫que el nombre de esta
‫propiedad aquí es el mismo que el
‫nombre de la variable, simplemente podemos hacerlo
‫como esto y deshacerse del nombre de la propiedad aquí, ¿de acuerdo?
‫Y también queremos obtener la contraseña.
‫Y entonces eso sería lo mismo,
‫entonces, básicamente, contraseña equivale a una solicitud. cuerpo. contraseña.
‫Y en lugar
‫de hacer eso, simplemente podemos usar el correo electrónico y la contraseña.
‫Y así, así, básicamente
‫creamos estas dos variables del objeto del cuerpo, ¿de acuerdo?
‫Básicamente, así es como el
‫usuario enviará las credenciales de inicio de sesión para que las verifiquemos.
‫De acuerdo, y ese proceso de verificación tiene un par de
‫pasos, así que vamos a
‫exponerlos aquí antes de comenzar a codificar.
‫Entonces, primero debemos verificar si el
‫correo electrónico y las contraseñas realmente existen, ¿de acuerdo?
‫Luego, número dos, verifique si el usuario existe y,
‫al mismo tiempo, si la contraseña es correcta.
‫Y luego, en tercer lugar,
‫si todo está bien, envíe el token, para que el token web JSON vuelva al cliente.
‫Bien, entonces el primero es muy fácil.
‫Entonces, si no hay correo electrónico o contraseña, queremos enviar
‫un mensaje de error a nuestro cliente.
‫Ahora, ¿cómo vamos a hacer eso?
‫Bueno, lo haremos usando
‫las herramientas que implementamos en la última sección.
‫Entonces, básicamente, el error de nuestra aplicación, ¿lo recuerdas?
‫Entonces, simplemente crearemos un nuevo error
‫aquí, y nuestro middleware de
‫manejo de errores globales lo detectará y enviará ese error al cliente.
‫Entonces, comencemos por importar ese error.
‫Así que recuerda que
‫es este de nuestra clase AppError, ¿verdad?
‫Así que digamos AppError y luego require.
‫Entonces, un nivel desde aquí, luego en las utilidades y luego
‫en appError.
‫De acuerdo, entonces tenemos que llamar al siguiente
‫middleware, y aquí es donde pasamos el error, ¿recuerdas eso?
‫Entonces, nuevo AppError, y el
‫mensaje es: proporcione un correo electrónico y una contraseña.
‫Y el código de error HTTP es 400 para una solicitud incorrecta, ¿de acuerdo?
‫Y ahora tengo algunos
‫errores aquí, así que, nuevamente, eslint me está ayudando a corregir algunos errores.
‫Entonces veo eso
‫aquí, nuevamente, lo usé con una T en lugar
‫de D, y si lo guardo ahora, todo está correcto.
‫Bien, entonces comencemos a probar esto de inmediato.
‫Y para eso,
‫simplemente comenzaré creando una ficha falsa aquí por ahora, está bien.
‫Entonces digamos res. estado 200 para ok.
‫JSON y, por supuesto, como
‫siempre, el estado establecido en éxito, y luego también nuestro token.
‫Y eso es todo lo que
‫enviaremos como respuesta al inicio de sesión, ¿de acuerdo?
‫Entonces, ningún usuario se opone porque eso no es necesario en absoluto.
‫Todo lo que queremos como respuesta para iniciar sesión es
‫en realidad el token.
‫Eso es todo lo que importa cuando el usuario inicia sesión.
‫A continuación, necesitamos implementar la ruta,
‫así que hagámoslo de nuevo en nuestro enrutador de usuario, ¿de acuerdo?
‫Y este es bastante similar a este.
‫Dupliquemoslo y luego reemplacemos el registro en
‫ambos casos aquí con el inicio de sesión, ¿de acuerdo?
‫Y nuevamente, esto solo es válido para una solicitud de publicación,
‫porque, por supuesto, queremos enviar las
‫credenciales de inicio de sesión en el cuerpo.
‫Y, de nuevo, es
‫una publicación, pero no una obtención, ni un parche, ni una
‫eliminación, porque eso no tiene ningún sentido en este caso.
‫Así que probémoslo de inmediato.
‫Copiaré esta URL aquí también.
‫Será publicación, inicio de sesión y luego el cuerpo,
‫por supuesto.
‫Así que de nuevo, raw y JSON.
‫Así que creemos un cuerpo simple aquí.
‫De acuerdo, intentemos enviar
‫esto sin contraseña y veamos qué sucede.
‫Y, de hecho, funciona, por lo que recibimos nuestro mensaje
‫aquí: proporcione un correo electrónico y una contraseña.
‫Y nuevamente, dado que estamos
‫en desarrollo, estamos obteniendo toda esta pila de
‫errores aquí, y el error completo con nuestro código
‫de estado, el estado y también, por supuesto,
‫diciendo que es un error operativo.
‫Así que tal como lo definimos en la sección anterior, ¿de acuerdo?
‫Ahora intentemos lo mismo con la contraseña.
‫Entonces, configúrelo en Creo que este era el indicado, y luego
‫deshágase del correo electrónico.
‫Y eso, por supuesto, desencadena lo mismo.
‫De acuerdo, y con todo correcto, debería funcionar, y
‫de hecho obtenemos nuestro token falso aquí.
‫Ahora solo una cosa que debemos hacer
‫aquí es regresar también.
‫Así que lo hicimos muchas veces
‫antes, y permítanme explicarlo nuevamente.
‫Es simplemente porque después de llamar al siguiente
‫middleware, queremos asegurarnos de que esta
‫función de inicio de sesión finalice de inmediato, ¿de acuerdo?
‫Y como no teníamos eso
‫antes, ahora verá este error aquí nuevamente.
‫Entonces esto ya es algo familiar en este punto, ¿verdad?
‫Por lo tanto, no se pueden establecer encabezados después de que
‫se envían al cliente, porque nuevamente, enviamos dos respuestas.
‫Primero la respuesta de error, y luego también este código aquí ejecutado, lo que por
‫supuesto no debería haber sido así.
‫Y de nuevo usamos return here para arreglar eso.
‫Bien, a continuación, verifiquemos si realmente hay un usuario para
‫el correo electrónico que se publicó.
‫Entonces, const user, y ahora usemos findOne en realidad, porque esta vez
‫no estamos seleccionando un usuario por la ID,
‫sino por su correo electrónico, ¿verdad?
‫Y entonces tenemos que pasar ese objeto
‫de filtro donde podemos decir correo electrónico igual a correo electrónico, ¿de acuerdo?
‫Entonces, el campo se llama correo
‫electrónico y la variable también se llama correo electrónico.
‫Y entonces en ES6 podemos abreviar eso simplemente así.
‫Ahora, antes de
‫que avancemos aquí, en realidad
‫hay algo importante que debo hacer por seguridad.
‫Déjame mostrarte eso en Postman,
‫donde nos registramos para una nueva cuenta.
‫Y entonces ven aquí que en la
‫salida del usuario, en realidad obtenemos la contraseña, ¿de acuerdo?
‫En realidad, está encriptado, pero
‫aún así, no es una buena práctica filtrar los
‫datos de la contraseña al cliente, ¿de acuerdo?
‫Por ejemplo, si tuviéramos que obtener todos los usuarios
‫aquí, y esta ruta aún no está
‫implementada, pero si tuviéramos que obtener todos los usuarios de
‫la colección, entonces todos tendrían
‫la contraseña visible, y no queremos eso. , ¿okey?
‫Y arreglarlo es bastante fácil, porque
‫lo hicimos antes.
‫Todo lo que tenemos que
‫hacer es decir la contraseña, luego seleccionarla y configurarla como falsa.
‫Y así, nunca aparecerá automáticamente en
‫ninguna salida.
‫Y dejar que
‫te lo demuestre registrándote como otra cosa, así.
‫Y veamos, oh, en realidad ya tenemos
‫un usuario con ese correo electrónico, así que hagamos test3.
‫Y ahora la contraseña todavía está aquí, pero supongo que
‫es simplemente porque podríamos crearlo, este nuevo documento aquí y,
‫por lo tanto,
‫no lo está seleccionando realmente porque en
‫realidad no lo estamos leyendo de la base de datos.
‫Así que implementemos
‫muy rápidamente esta ruta
‫aquí, eso solo tomará un minuto.
‫Entonces, solo para ver si esto de aquí realmente funciona.
‫Porque este paso es realmente
‫importante para lo que haremos a continuación, ¿de acuerdo?
‫Así que sigamos adelante y copiemos este código de getAllTours,
‫y no copiemos realmente, pero lo que vamos a
‫hacer es bastante similar a esto, por supuesto.
‫Así que simplemente copiaré esto,
‫pero sin las funciones de la API.
‫Así que no estoy realmente interesado en eso, ¿de acuerdo?
‫Y este lo voy a
‫hacer en el controlador de usuario, porque obtener todos los usuarios no
‫tiene nada que ver con la autenticación.
‫Entonces, en este caso, el
‫controlador de usuario es el lugar perfecto para hacer esto.
‫Ahora, por supuesto, también necesito importar
‫rápidamente el modelo de usuario aquí, así que
‫copiemos también eso del tourController, que no es el mismo, pero sí similar.
‫Así que tomemos todo esto en nuestro
‫controlador de usuario.
‫Este no lo necesitamos, pero también necesitamos catchAsync.
‫Bien, entonces tour será
‫usuario, y aquí estará userModel, ¿de acuerdo?
‫Luego aquí tenemos recorridos, y aquí abajo y aquí abajo,
‫así que presiono Comando + D para
‫seleccionarlos todos y cambiarlos a usuarios.
‫Y luego aquí, por supuesto, lo que estamos esperando es Usuario. encontrar.
‫Ahora que
‫tenemos una espera aquí, necesitamos async, luego también necesitamos next, y dado
‫que tenemos una async
‫aquí, deberíamos envolver todo en una captura asincrónica.
‫Así que hagámoslo.
‫De acuerdo, y en
‫este punto esto ya no es nada
‫nuevo para ti, ya estamos acostumbrados a hacer este tipo de cosas, ¿verdad?
‫Entonces, esto ya debería funcionar en este
‫punto, así que probémoslo.
‫Entonces, obtenga todos los usuarios, y ahora, de hecho, vemos que
‫su contraseña no está incluida en la salida.
‫Y eso es importante porque en realidad, en el hallazgo,
‫aquí en el AuthController,
‫esto aquí ahora tampoco contendrá la contraseña, ¿de acuerdo?
‫Y entonces la salida
‫de esto aquí ahora tampoco contendrá la contraseña.
‫Pero necesitamos la contraseña para verificar si
‫es correcta, ¿verdad?
‫Y, por lo tanto, también debemos seleccionarlo explícitamente.
‫Entonces, ¿recuerdas cómo usamos select antes para
‫básicamente seleccionar un par de campos
‫de la base de datos, solo los que necesitábamos?
‫Ahora, en este caso, cuando queremos
‫que el campo que está por defecto no
‫esté seleccionado, necesitamos user plus y luego el nombre del campo.
‫Entonces contraseña en este caso.
‫Y así, volverá a aparecer en la salida, ¿de acuerdo?
‫Por supuesto, debemos esperar esta
‫consulta y luego marcar la función como asíncrona.
‫Y luego,
‫como antes, para evitar ese feo bloque
‫try catch, envuelva toda esta función en catchAsync.
‫Está bien, ¿tiene sentido?
‫Así que ahora, rápidamente, registremos al usuario en la consola, solo para
‫ver si funciona.
‫Así que este de aquí.
‫Obtenemos nuestro éxito y, de hecho, tenemos al usuario aquí.
‫Entonces, exactamente con el correo electrónico que acabo de
‫publicar, y luego la contraseña ahora también se incluye
‫en la salida, ¿de acuerdo?
‫Y nuevamente, eso es porque lo seleccionamos explícitamente aquí.
‫Y ahora es el momento de comparar las contraseñas
‫que tenemos en la base de datos con
‫la que acaba de publicar el usuario.
‫Pero, ¿cómo vamos a hacer eso?
‫Porque, por ejemplo, la contraseña puede
‫ser, o es en este ejemplo, pass1234, pero
‫la que tenemos almacenada en el documento se ve así.
‫Entonces, ¿cómo vamos a comparar esto?
‫Realmente no hay una forma de hacerlo, ¿verdad?
‫Pero en realidad lo hay, todo lo
‫que tenemos que hacer es volver a usar el paquete bcrypt, ¿de acuerdo?
‫Entonces usamos bcrypt para generar esta contraseña hash, y también
‫podemos usar el mismo paquete
‫para comparar básicamente una contraseña original como esta aquí con
‫la contraseña hash.
‫Por supuesto, esta contraseña aquí, ya que está encriptada, no
‫hay forma de recuperar la anterior,
‫así que la contraseña original de esta cadena, ¿verdad?
‫Así que ese
‫es el objetivo de cifrar una contraseña.
‫Entonces, la única
‫forma de hacerlo es que este
‫paquete para este algoritmo encripte esta contraseña
‫también y luego la compare con la encriptada, ¿de acuerdo?
‫Así que implementemos una función que hará
‫eso, y para eso usaremos, nuevamente, el paquete bcrypt.
‫Y lo haremos en el modelo de usuario.
‫Y podría preguntar "Por qué lo estamos haciendo
‫en un modelo" y no solo aquí ", pero
‫eso es, nuevamente, porque esto está realmente relacionado con los datos en sí.
‫Y también ya tenemos ese paquete allí, por lo
‫que es más fácil simplemente hacerlo allí.
‫Así que eliminemos esta cadena y luego creemos
‫una función aquí, que
‫verificará si la contraseña dada es
‫la misma que la almacenada en el documento.
‫Entonces, por primera vez, crearemos algo
‫llamado método de instancia.
‫Entonces, un método de instancia es básicamente un método
‫que estará disponible en todos los documentos
‫de una determinada colección, ¿de acuerdo?
‫Y funciona así.
‫Entonces, nuevamente, está definido en
‫un userSchema, y luego decimos métodos, y ahora, en este
‫caso, queremos llamar a la función correctPassword, ¿de acuerdo?
‫Entonces función, ahora esta función aceptará una contraseña
‫candidata, entonces la contraseña que
‫el usuario pasa en el cuerpo,
‫y luego también la contraseña de usuario, ¿de acuerdo?
‫Ahora, dentro de estos métodos instanciados,
‫dado que están disponibles en el documento,
‫la palabra clave this en realidad apunta al documento actual.
‫Pero en este caso, dado
‫que tenemos la contraseña configurada para seleccionar falsa, esto aquí, ¿recuerdas?
‫Está bien,
‫y por eso, esto. la contraseña no estará disponible.
‫Entonces, idealmente lo haríamos así, y de esta
‫manera solo necesitaríamos
‫pasar la contraseña candidata y no la contraseña de usuario.
‫Pero nuevamente, en este momento eso no
‫es posible porque la contraseña no está disponible en la salida.
‫Y es
‫por eso que también tenemos que pasar userPassword.
‫Entonces, el objetivo
‫de esta función es devolver solo verdadero o falso.
‫Así que básicamente es cierto si las contraseñas son las
‫mismas y falso si no.
‫Entonces regrese, y luego bcrypt que ya conocemos, y
‫luego usaremos la función de comparación, ¿de acuerdo?
‫Y la función de comparación es
‫realmente fácil, todo lo que necesitamos es
‫pasar el candidatoPassword y el userPassword, no userSchema, userPassword, ¿de acuerdo?
‫Y al igual que la función
‫hash aquí arriba, esta también es una función asincrónica.
‫Y así, al igual que antes, usamos await, y luego aquí async.
‫Vale, ¿tiene sentido?
‫Entonces, nuevamente, esta función de comparación
‫aquí simplemente devolverá verdadero
‫si estas dos contraseñas aquí son iguales, y falso si no.
‫Y nuevamente, no podemos compararlos
‫manualmente porque la contraseña candidata no tiene hash,
‫por lo que es la contraseña original
‫que proviene del usuario, pero userPassword, por
‫supuesto, está hash, por lo que sin
‫esta función aquí no tendríamos forma de compararlos, ¿de acuerdo?
‫Entonces aquí devolvemos verdadero o falso, ahora
‫todo lo que tenemos que hacer es llamar a
‫esta función en el authController.
‫Cerremos un par de estos.
‫Muy bien, esto de aquí fue solo para una demostración.
‫Así que simplemente llamemos a esto aquí
‫correcto, y ahora recordemos que la función que acabamos de
‫definir es un método instanciado.
‫Por tanto, está disponible en todos los documentos de usuario.
‫Entonces, esta variable aquí ahora mismo es
‫un documento de usuario, ¿verdad?
‫Porque es el resultado de consultar el modelo de usuario.
‫Y ahora podemos decir usuario. correctPassword.
‫Ahora todo lo que tenemos que hacer es pasar la contraseña del
‫candidato, que es la contraseña, recuerde.
‫Entonces este de aquí, y luego el userPassword.
‫Y eso está en user. contraseña, ¿de acuerdo?
‫Y entonces esto aquí ahora será verdadero o falso.
‫Muy bien, y ahora usemos estas dos variables
‫para averiguar si el usuario existe y
‫la contraseña es correcta.
‫Así que ya nos
‫dimos cuenta de eso, pero ahora necesitamos escribir nuestra declaración if.
‫Entonces, si no hay un usuario, o la contraseña es incorrecta, entonces básicamente
‫correcto es falso, que es lo que esto significa.
‫En ese caso, queremos, nuevamente, regresar e ir
‫directamente a nuestro próximo middleware con un nuevo
‫AppError.
‫Y en este caso decir correo electrónico o contraseña incorrectos, ¿de acuerdo?
‫Y luego el código de estado
‫es 401, lo que significa no autorizado, ¿de acuerdo?
‫Ahora podríamos haber hecho esto por
‫separado, así que primero verificamos
‫al usuario y luego verificamos la contraseña correcta.
‫Pero en ese caso, le daríamos a un posible
‫atacante información sobre si el correo electrónico o la contraseña son incorrectos.
‫Y de esta manera aquí es un poco más vago.
‫Así que no estamos especificando realmente qué es incorrecto aquí.
‫Entonces, si es correo electrónico o si es la contraseña.
‫Entonces, si algún atacante
‫intenta ingresar datos aleatorios, entonces no sabrá
‫si el correo electrónico existe realmente, o
‫si es solo la contraseña la que está mal, ¿de acuerdo?
‫Ahora solo dos cosas aquí, y la
‫primera es que realmente necesitamos
‫esperar esta función asincrónica, ¿de acuerdo?
‫Así que recuerde que la contraseña correcta
‫es una función asincrónica, por
‫lo que aquí también debemos esperar por eso.
‫Y luego también hay otro problema,
‫debido a que este usuario no existe aquí,
‫entonces esta siguiente línea de código realmente no se puede ejecutar, ¿de acuerdo?
‫Porque, por ejemplo, user. la contraseña
‫no estará disponible.
‫Entonces, en realidad, necesitamos mover todo esto, o en
‫realidad solo este código,
‫lo vamos a mover aquí a la declaración if else.
‫Muy bien, y de esta manera, si
‫el usuario no existe, entonces si esto es cierto, entonces
‫ni siquiera ejecutará este código aquí, y
‫entonces no habrá ningún problema.
‫Pero si el usuario existe, también ejecutará este código
‫y comprobará si la contraseña es correcta.
‫Entonces, si la contraseña
‫es correcta, solo en ese caso llegaremos a este código aquí.
‫Entonces esa es toda la idea de esta función.
‫Básicamente, buscamos los negativos.
‫Entonces, si no hay correo electrónico ni
‫contraseña, obtenemos este error.
‫Si no hay ningún usuario o si hay una
‫contraseña incorrecta, cree este error.
‫Pero si no hubo ningún error,
‫bueno, en ese caso llegamos a esta parte del código,
‫donde ahora generamos un
‫token y luego se lo enviamos al usuario.
‫Ahora, crear este token será exactamente lo mismo
‫que hicimos antes aquí.
‫Entonces, en lugar de repetir
‫todo este código, creemos una función para esto, ¿de acuerdo?
‫Así que voy
‫a copiar este código, y luego aquí muy rápidamente
‫const signToken, que recibirá como única entrada la ID de usuario.
‫Entonces ID, y luego devolverá el token, ¿de acuerdo?
‫Así que simplemente lo firmará y lo devolverá de inmediato.
‫Muy bien, y aquí lo
‫que vamos a hacer es firmar y pasar esta identificación.
‫Y luego, por supuesto, también tenemos que cambiarlo aquí, ¿de acuerdo?
‫Por lo tanto, establezca esta ID en esta ID,
‫que, como ya sabe en este momento, es la misma que esta.
‫Muy bien, entonces sigamos adelante y hagamos lo mismo aquí.
‫signToken, y luego en este caso es user. _identificación.
‫Uf, esa fue una
‫conferencia larga, ahora sigamos adelante y probémosla.
‫Entonces ahora debería funcionar.
‫Así que recuerde, bueno, este es ahora otro usuario, pero sé que
‫esta es la contraseña que usé.
‫Así que, por ahora, usemos
‫otro, así que solo prueba12 y veamos qué sucede.
‫Esperemos, y de hecho, correo electrónico o contraseña incorrectos.
‫Y así obtenemos, por supuesto, también nuestro
‫401, y de hecho significa que nuestro código ya está probando
‫la contraseña.
‫Entonces, si ahora elegimos la contraseña correcta,
‫debería pasar, así que esperemos.
‫Pero seguimos recibiendo este error.
‫Bueno, simplemente eliminémonos de todos nuestros usuarios aquí, solo para
‫asegurarnos de que no tenga que
‫hacer nada con los usuarios, y luego creemos uno nuevo, de acuerdo.
‫Así que hola @ jonas, y pasa1234.
‫Sigamos adelante y copiemos, lo enviaré ahora.
‫Y estas credenciales ahora son correctas con seguridad.
‫Entonces, si ahora no funciona, hay algún tipo de error.
‫Pero ahora realmente funcionó, genial.
‫Entonces, aquí está nuestro token, y se ve exactamente como antes, por lo
‫que podemos asumir que es correcto.
‫Ahora intentémoslo de nuevo, eliminando esto, está bien.
‫Y así, con la contraseña incorrecta, no funciona.
‫Pongamos ahora la contraseña correcta y el correo electrónico incorrecto, y luego
‫obtenemos el mismo error.
‫Correo electrónico o contraseña incorrectos.
‫Y nuevamente, si devolvemos todo, obtenemos
‫una nueva ficha.
‫Y ahora estamos realmente conectados a la aplicación.
‫Y sé que esto puede parecer algo abstracto, por
‫lo que todo lo que realmente obtenemos es un token, y eso
‫significa que estamos conectados a la aplicación, ¿verdad?
‫Pero, bueno, así es como funciona la autenticación sin estado.
‫Más adelante, cuando realmente creemos el sitio web
‫dinámico, entonces, por supuesto, será mucho
‫más visible si el usuario está conectado o desconectado, ¿verdad?
‫Pero seguirá funcionando detrás de escena
‫usando este mismo token.
‫Entonces, si no hay un token, entonces el sitio web se verá de una
‫manera, y si hay un token, el
‫sitio web se verá de otra manera, con la
‫imagen del usuario y el nombre de usuario allí mismo en el sitio web, por ejemplo.
‫De todos modos, esta fue una conferencia bastante larga.
‫Asegúrate de volver aquí y de intentar
‫comprender todo este código que tenemos aquí, ¿de acuerdo?
‫Porque sé que algo de
‫esto puede resultar un poco confuso.
‫Por ejemplo, esta función correctPassword aquí.
‫Y luego estoy seguro de que todo tendrá sentido para ti.