No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Ruta de login

10/22
Recursos

¿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:

@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.

¿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.

  1. Configura el endpoint:

    • Método: POST
    • URL: http://localhost:<puerto>/out/login
    • Cuerpo:
      {
        "email": "[email protected]",
        "password": "tucontraseña"
      }
      
  2. 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.

  3. 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:

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

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

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 } )

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:

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

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.

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”.

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
nest g co auth/controllers/auth --flat

Usando Postgrest con Swagger

Usuario con datos erróneos:

Usuario con datos correctos:

Configurar VSCODE para hacer debugging en NestJs

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

👏