¿Cómo probar la implementación de login en un proyecto NestJS?
¡Finalmente es el momento de poner a prueba nuestro sistema de autenticación! En este artículo te explicaremos cómo verificar si la implementación de login en un proyecto NestJS ha sido exitosa utilizando un controlador y probándola con herramientas como Insomnia. Este proceso es esencial para garantizar que el sistema de autenticación funcione correctamente y proporcione la respuesta adecuada a los usuarios.
¿Cómo crear un controlador en NestJS?
Para comenzar, necesitamos definir un controlador de login. Usaremos el generador de NestJS con el siguiente comando:
nest generate controller controllers/out --flat
Este comando crea un controlador llamado OutController y lo integra como parte del módulo outModule. En el controlador, creamos un nuevo método de tipo POST que recibirá el identificador y contraseña del usuario.
Utilizamos el decorador @Req para obtener el request y lo manejamos como un tipo de request de Express. Allí validamos las credenciales ingresadas y, si son correctas, devolvemos el usuario autenticado.
¿Cómo proteger las rutas con guardias en NestJS?
Nuestra implementación de login también requiere validar las credenciales de usuario de manera segura. Para esto, utilizamos los guardianes de seguridad con la estrategia de autenticación que definimos:
Aquí aplicamos AuthGuard para proteger el endpoint, utilizando la estrategia llamada local. Si las credenciales del usuario coinciden, el guardián permite que el proceso de autenticación continúe, devolviendo el usuario. De lo contrario, deniega el acceso.
¿Cómo probar el endpoint de login con Insomnia?
Una vez que el controlador y la seguridad están configurados, es momento de probar la funcionalidad. Utilizamos una herramienta como Insomnia para enviar solicitudes y verificar la respuesta del servidor.
Prueba con credenciales correctas: Envía la solicitud con un email y contraseña válidos y verifica que recibas el objeto usuario como respuesta.
Prueba con credenciales incorrectas: Cambia el password y el email para verificar el manejo de errores y asegúrate de recibir un mensaje de no autorizado o error 500 dependiendo del caso.
¿Cómo resolver problemas comunes?
Durante las pruebas, podrías enfrentar algunos problemas comunes:
Sin retorno de mensaje: Asegúrate de que estás agregando await en las promesas para capturar correctamente las excepciones.
Manejo de valores nulos: Si recibes un error relacionado con un valor nulo, como al intentar obtener la propiedad password de un usuario que no existe, ajusta la lógica para validar primero la existencia del usuario antes de acceder a sus propiedades.
¿Cómo personalizar los campos de usuario en la estrategia local?
Podemos personalizar los nombres de los campos que utiliza nuestra estrategia de autenticación. Por defecto, usa username y password, pero podemos cambiarlos mediante el siguiente ajuste en la LocalStrategy:
Estos cambios permiten enviar credenciales con nombres personalizados, como email, sin romper el funcionamiento del sistema de autenticación.
Este recorrido por la configuración y prueba del sistema de login en NestJS es una guía esencial para asegurar que tu aplicación maneje de manera efectiva la autenticación de usuarios. Invitamos a seguir experimentando y ajustando las configuraciones para adaptarse a las necesidades de tu proyecto. ¡Es un paso significativo en el dominio del desarrollo seguro y eficiente con NestJS!
Sí lo es, pero para agilizar la clase imagino qué no lo hicieron. Saludos Carlos.
Me parece que lo que estas diciendo es para generar un token, y hasta el momento en la clase estamos implementando la strategy de passport para permitir solo el logueo, buen aporte!
Creo que el codigo del final del metodo validateUser quedaria mas limpio si lo tenemos de la siguiente manera:
En vez de usar if's nidados, mejor tenemos un early return. Asi nos aseguramos que user exista y podemos acceder a user.password.
Pues en tu código usas if anidados, de la manera en la que enseña el profesor es delegando a una estrucutra de carpeta responsabilidades diferentes
En mongo puedes pasar un segundo parametro al find o findOne que te permite ocultar o mostrar los campos que te devuelve la consulta, para el caso seria:
userModel.find({email: email },{password:0})
En mongo a esto se le llama "proyección".
Se pasa un objeto en donde las claves son los atributos del documento y en los valores se indica con 0 si se desea excluir de la respuesta a la consulta o 1 en caso contrario (se "proyecta" cuales datos se incluyen en la respuesta de la consulta).
Pero en este caso de uso no se puede excluir el password, porque precisamente contiene el hash que se necesita en el método validateUser del servicio de autenticacion para compararlo por el password ingresado.
Buenas, el Login no me funcionaba
Estuve teniendo un 401 "not allow" al intentar loguearme y lo corregí de la siguiente manera.
El problema que tenía venía de src/users/users.service.ts
En el código que venimos trabajando la conuslta por email está de la siguiente forma:
Mi problema era que nunca podía loguearme, la razon es que la consulta a la BD es una promesa que no se resolvía antes de continuar con el flujo. Obteniendo siempre un user=null.
Desde entonces la tengo como una función asíncrona. Ej:
De ésta forma resolví el problema que tenía. Lo dejo acá por si alguno tiene el mismo problema.
Saludos
😀 Me ahorraste colocar console.log, Gracias
Efectivamente, toda operación que haga consultas a bbdd, debe ser asíncrona. Xq no sabemos el tiempo de respuesta de la misma. Y sino ponemos asyn--await el flujo del código sigue su marcha.
por que el endpoint de login( ... ) usa un @Req() en vez de un @Body()?
Error en la salida (req.user) Property "user" doesn't exist
Me salio este error cuando intente de ejecutar la aplicación, encontre que tenia que ver con el namespace, pero las soluciones estaban muy confusas, pero en la documentación hay otro metodo sin usar express.
Si se envía el campo email vacío, o simplemente no se envía, ¿Cuál es el motivo por el cual nuestro mensaje personalizado no se logra mostrar? Alguien mas a tenido este problema? Gracias.
me sale este error
Property 'user' does not exist on type 'Request<ParamsDictionary, any, any, ParsedQs, Reco
exist on type 'Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>'.
11 return req.user;
mira usa este codigo en el controlador, en ves de utilizar express, utiliza Request en el common.
Para evitar enviar información sensible en mi respuesta del controlador (passwords, etc) la mejor técnica es serializar, defino desde mi entity que info no debo compartir asi evito tener que hacer modificaciones de mi respuesta en cada enpoint https://platzi.com/clases/2282-nestjs-typeorm/37326-serializar/
Esta Clase, al finalizar....salta a "Configurando mongo atlas" y no a "conectando passport con jwt".
¿A alguin también le pasa que al importar el AuthModule en el AppModule, nest da un error de resolver de'pendencias porque dice que no úede resolver las de product service en el usersservice?
Que raro no debería darte error parece un error de no exportar esos servicios en los módulos necesarios, ¿puedes compartirnos tu repositorio?
al hacer el post en el auth/login me envia de respuesta el password haseado y no devuelve el createAt y el updateAt
Cuando hice Post con insomnia siempre daba un 401, hice un script con axios haciendo la misma petición y si me funcionaba.
Hasta ahora encuentro cual es el error. Alguna idea?
Es un gran curso, pero estoy estancado, hice el codigo tal cual hasta el minuto 6:12 pero cuando pongo un email y una contraseña valida me sigue mostrando error 401 y no me deja loguear, estoy con el modelo TypeORM, no se en que puedo estar fallando.
Lo hiciste con Insomnia? porque yo lo hice y me daba el error 401 siempre, asi que hice un script con axios para probar y si me funcionaba
No me cambia el mensaje de error de autenticación
// local.strategy.tsimport{Injectable,UnauthorizedException}from'@nestjs/common';import{PassportStrategy}from'@nestjs/passport';import{AuthService}from'../services/auth.service';import{Strategy}from'passport-local';@Injectable()exportclassLocalStrategyextendsPassportStrategy(Strategy){constructor(privateauthService:AuthService){super({usernameField:'email',passwordField:'password',});}asyncvalidate(email: string,password: string){const user =this.authService.validateUser(email, password);console.log('pasa 1');if(!user)thrownewUnauthorizedException('user.unauthorized');console.log('pasa 2');return user;}}
A la fecha hay una respuesta automática cuando falla la validación, con la implementación de UnauthorizedException está retornando un mensaje de error duplicado:
Recomiendo que para los strategies en cada uno crear una constante con el nombre de su strategy y importarlo en donde lo necesiten usar, así evitan problemas de tipeo o cambio de nombre