No es mejor practica crear un LoginDto?
login(@Body() loginDTO: LoginDto) {
return this.authService.generateJWT(loginDTO);
}
Introducción
Cómo autenticar usuarios con NestJS
Instalación de PlatziStore con MongoDB
Instalación de PlatziStore con TypeORM
Protección con guardianes
Introducción a Guards
Usando un decorador
Guard con variables de ambiente
Autenticación con Passport
Hashing de contraseñas en TypeORM
Hashing de contraseñas en MongoDB
Autenticación con Passport.js
Ruta de login
Autenticación con JSON Web Tokens
Conectando Passport con JWT
Secret desde variables de entorno
Implementando JWT Guard
Extendiendo JWT Guard
Control de roles en NestJS
Obteniendo órdenes del perfil
Deployment
Configurando Mongo Atlas
Deploy de Mongo en Heroku
Configuración de PostgreSQL en Heroku
Deploy de Postgres en Heroku
Corriendo migraciones de Postgres en Heroku
Próximos pasos
¿Quieres más cursos de NestJS?
No tienes acceso a esta clase
¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera
¡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.
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.
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:
@UseGuards(AuthGuard('local'))
@Post('login')
async login(@Req() req) {
return req.user;
}
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.
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.
Configura el endpoint:
POST
http://localhost:<puerto>/out/login
{
"email": "[email protected]",
"password": "tucontraseña"
}
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.
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.
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
:
super({
usernameField: 'email',
passwordField: 'password'
});
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!
Aportes 18
Preguntas 10
No es mejor practica crear un LoginDto?
login(@Body() loginDTO: LoginDto) {
return this.authService.generateJWT(loginDTO);
}
Creo que el codigo del final del metodo validateUser quedaria mas limpio si lo tenemos de la siguiente manera:
async validateUser(email: string, password: string) {
const user = await this.usersService.findByEmail(email)
if (!user) {
return null
}
const isMatch = await bcrypt.compare(password, user.password)
if (isMatch) {
const { password, ...rta } = user.toJSON()
return rta
}
return null
}
En vez de usar if’s nidados, mejor tenemos un early return. Asi nos aseguramos que user exista y podemos acceder a user.password.
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 } )
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:
findByEmail(email: string) {
return this.userModel.findOne({ email }).exec();
}
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:
async findByEmail(email: string) {
const userEncontrado = await this.userModel.findOne({ email }).exec();
return userEncontrado;
}
De ésta forma resolví el problema que tenía. Lo dejo acá por si alguno tiene el mismo problema.
Saludos
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.
import { Controller, Post, Request, UseGuards } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
@Controller('auth')
export class AuthController {
@UseGuards(AuthGuard('local'))
@Post()
login(@Request() req) {
return req.user;
}
}
En el controlador importamos de common el decorador Request y nuestra función login colocamos el decorador con el “req” y funciona igual. 😸
nest generate controller auth/controllers/auth
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”.
Usando Postgrest con Swagger
Usuario con datos erróneos:
Usuario con datos correctos:
Para configurar vscode para debuguear el código, demos:
Ir a a la pestaña Run and Debug o Ctrl + Shipt + D
Crear un archivo launch.json con create a launch.json file.
Pegar el siguiente código.
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Launch Program",
"skipFiles": [
"<node_internals>/**"
],
"program": "${file}",
"preLaunchTask": "tsc: build - tsconfig.json",
"outFiles": [
"${workspaceFolder}/dist/**/*.js"
]
}
]
}
En la pestaña Run and Debug abrimos la lista desplegable, seleccionamos Run Script: start:debug
Dejamos que nuestro sistema transpile.
Una vez terminada la transpilación, podremos debuguear nuestro código con nestjs.
nest g co auth/controllers/auth --flat
hice una cambio de estructura en la validación para una mejor lectura del código, se las comparto:
async validateUSer(email: string, password: string) {
const user = await this.userService.findByEmail(email);
if (!user) {
return null;
}
const isMatch = await bcrypt.compare(password, user.password);
return isMatch ? user : null;
}
Les dejo por aca algo que hice para reciba email o username, espero les sirva
async findOneByEmailOrUsername(username: string) {
const type = isEmail(username);
if (type) {
return await this.usersRepository.findOne({
where: { email: username },
});
}
if (!type) {
return await this.usersRepository.findOne({
where: { username },
});
}
}
Login exitoso
Login fallido
👏
¿Quieres ver más aportes, preguntas y respuestas de la comunidad?