Crea una cuenta o inicia sesión

¡Continúa aprendiendo sin ningún costo! Únete y comienza a potenciar tu carrera

Convierte tus certificados en títulos universitarios en USA

Antes: $249

Currency
$209

Paga en 4 cuotas sin intereses

Paga en 4 cuotas sin intereses
Suscríbete

Termina en:

18 Días
23 Hrs
7 Min
16 Seg

Introducción a Guards

4/22
Recursos

Aportes 8

Preguntas 1

Ordenar por:

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

Por si alguno tiene dudas sobre qué se puede hacer una vez el proyecto esté completado…
Les dejo un proyecto propio de fake ecommerce que usa como base esta API (tiene modificaciones y detalles para adaptarla a mi proyecto). El frontend está hecho con React
Link aquí
Saludos a todos

Lo que hicimos

Esto aplica para ambos proyectos

nest g mo auth
nest g gu auth/guards/apiKey --flat

Configuración del guard

import { CanActivate, ExecutionContext, Injectable, UnauthorizedException } from '@nestjs/common';
import { Observable } from 'rxjs';
import { Request } from 'express';

@Injectable()
export class ApiKeyGuard implements CanActivate {
  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    const request = context.switchToHttp().getRequest<Request>();
    const authHeader = request.header('Auth');
    const isAuth = authHeader === '1234';
    if (!isAuth) {
      throw new UnauthorizedException('not allow');
    }
    return true;
  }
}

Protegiendo una ruta

@UseGuards(ApiKeyGuard)
  @Get('nuevo')
  newEndpoint() {
    return 'yo soy nuevo';
  }

Introducción a Guards

Los guards (guardianes) son artefactos proporcionados por Nest.js que nos permiten proteger nuestros Endpoints en los controladores, verificando si un usuario tiene los permisos necesarios para acceder a un Endpoint en función de una condición.

Esto se puede lograr validando datos en los encabezados (headers), el estado, un token, etc., y determinando si el usuario tiene los permisos necesarios o no.

Creando la capa de autenticación

Primero lo que vamos a hacer es crear un nuevo guardián que se va a encargar de la autenticación en nuestra aplicación.

nest g gu auth/guards/api-key --flat

La estructura que nos debería crear es la siguiente:

import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Observable } from 'rxjs';

@Injectable()
export class ApiKeyGuard implements CanActivate {
  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    // Si retornamos true, se permite el acceso al Endpoint
    return false;
  }
}

Ahora, veamos cómo podemos implementar este guardián. Vamos a seleccionar un controlador y un Endpoint específico, y lo protegeremos con nuestro guardián:

  • **src/app-controller.ts**:
// Importamos el guardián y el decorador para usarlo
import { UseGuards } from '@nestjs/common';
import { ApiKeyGuard } from './auth/guards/api-key.guard';

@Controller()
export class AppController {
  // ...

  // Pasamos el guardián al decorador
  @UseGuards(ApiKeyGuard)
  @Get('nuevo')
  newEndpoint() {
    return 'Yo soy nuevo';
  }
}

De esta manera, no será posible acceder al Endpoint, ya que en el guardián estamos retornando false, lo cual deniega el acceso.

Podemos permitir el acceso si se envía un encabezado específico. Veamos cómo hacerlo en nuestro guardián:

// Importamos el tipo 'Request'
import { Request } from 'express';

@Injectable()
export class ApiKeyGuard implements CanActivate {
  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    // Obtenemos la información del request HTTP
    const request = context.switchToHttp().getRequest<Request>();
    // Obtenemos el encabezado 'Auth' de ese request
    const authHeader = request.header('Auth');

    // Si no se envía ninguna autenticación, se rechaza el acceso
    if (!authHeader) {
      return false;
    }
    
    // Verificamos si el encabezado 'Auth' es igual a 'contraseña ultra secreta'
    // Si es así, se permite el acceso; de lo contrario, se deniega el acceso
    return authHeader === 'contraseña ultra secreta';
  }
}

Si enviamos la clave correcta en el encabezado ‘Auth’, se nos permitirá el acceso; de lo contrario, recibiremos un código 403, que indica que no estamos autorizados.

De esta manera, podemos utilizar los guardián en Nest.js. Como has visto, los guardián se basan en un contexto que nos permite acceder a la información necesaria y podemos programar la condición que debe cumplir esta información obtenida a través del contexto.

me gustan las clases de Nicolas, pero esta nueva metodología de retomar código de otros cursos no.

Por cierto, el @UseGuard( … ) hay que importarlo de
import { …, UseGuards } from ‘@nestjs/common’;

4 artefactos principales en el framework


Controllers

Providers

Pipes

Guards


Los Guards nos ayudarán a brindar cada uno de los endpoints en el controller, y nos dirá si tiene o no autorización.


Ejecutamos nest g mo auth para crear un módulo nuevo, y generamos el guard con nest g gu auth/guards/apiKey —flat

Nest es muy hermoso! Que gran framework!!! ❤️

Error usando guards y @Res en el mismo Endpoint
Quiero compartir un error que tuve al estar usando un guard y @Res en el mismo endpoint.

Guard

@Injectable()
export class ApiKeyGuard implements CanActivate {
  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    const request = context.switchToHttp().getRequest<Request>();
    const authHeader = request.header('auth') || '';
    if (authHeader !== '123456') {
      throw new UnauthorizedException('User Unathorized');
    }
    return true;
  }
}

Controller


  @HttpCode(HttpStatus.ACCEPTED) 
  @Get('new')
  @UseGuards(ApiKeyGuard)
  newEndpoint(@Res() res: Response) {
    return res.json({ message: 'new world!' });
  }

Según mi teoría al estar usando el mismo request en el guard y el controller causaba este error

Error: This is caused by either a bug in Node.js or incorrect usage of Node.js internals.
node_app_dev  | Please open an issue with this stack trace at https://github.com/nodejs/node/issues
node_app_dev  | 
node_app_dev  |     at new NodeError (node:internal/errors:387:5)
node_app_dev  |     at assert (node:internal/assert:14:11)
[3:22:57 PM] File change detected. Starting incremental compilation...
[3:23:13 PM] File change detected. Starting incremental compilation...

Y se soluciono simplmente cambiando la forma de la respuesta en el controller

  @HttpCode(HttpStatus.ACCEPTED) // 👈 Using decorator
  @Get('new')
  @UseGuards(ApiKeyGuard)
  newEndpoint() {
    return { message: 'new world!' };
  }

Por defecto la respuesta se manda como JSON así que no es necesario usar res.json().

Vengo de express y por eso estaba tratando de usar esa misma forma.