Crea una cuenta o inicia sesi贸n

隆Contin煤a aprendiendo sin ning煤n costo! 脷nete y comienza a potenciar tu carrera

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 鈥楢uth鈥, 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 鈥攆lat

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.