Create an account or log in

Keep learning for free! Join and start boosting your career

Aprovecha el precio especial y haz tu profesión a prueba de IA

Antes: $249

Currency
$209
Suscríbete

Termina en:

1 Días
23 Hrs
23 Min
27 Seg

Usando un decorador

5/22
Resources

How to protect and make public endpoints in NestJS?

NestJS offers an effective way to protect endpoints using guardians and decorators. This allows you to programmatically determine which endpoints require authentication and which can be made public. In this article, we will explore how to use these features in your NestJS controllers.

How to apply guards to endpoints?

Endpoint protection in NestJS can be handled using guards. Guards are applied using decorators. They allow to protect all endpoints of a controller by applying the decorator at the class level. This means that any request to an endpoint within that controller must meet the specified conditions to be authorized.

@UseGuards(YourGuard)export class YourController { // All endpoints are protected}

This mechanism often uses an authorization token in the request headers, as in the example where it is specified that all endpoints require a valid Authorization header.

How to make an endpoint public using setMetadata?

To release an endpoint from the control of a gatekeeper, you can use the setMetadata function. This decorator can indicate that a specific endpoint is public and therefore does not need to comply with the same restrictions.

First, import SetMetadata from the @nestjs/common module:

import { SetMetadata } from '@nestjs/common';

Then, apply it to the endpoint you want to make public:

@Get('public-endpoint')@SetMetadata('isPublic', true)publicEndpoint() { // This endpoint is public}

The @SetMetadata decorator allows assigning metadata to an endpoint, specifying that it is public. Thus, even though the controller is protected by a guardian, this specific endpoint will be excluded from the restriction.

How to use Reflector to read metadata?

For a guardian to ignore an endpoint, it must check the metadata of the endpoint at runtime. This can be done by using the NestJS Reflector, which allows accessing the metadata defined in an endpoint.

import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';import { Reflector } from '@nestjs/core';
@Injectable()export class YourGuard implements CanActivate { constructor(private reflector: Reflector) {}
 canActivate(context: ExecutionContext): boolean { const isPublic = this.reflector.get<boolean>('isPublic', context.getHandler()); if (isPublic) { return true; // Allow access, since it is a public endpoint } // Apply normal authorization logic } }

How to create a custom decorator for public endpoints?

To avoid errors and improve maintainability, it is recommended to create a custom decorator that abbreviates the use of setMetadata. This decorator is used to mark an endpoint specifically as public.

  1. Create a file for the decorator:
  2. Import SetMetadata and define the decorator:
import { SetMetadata } from '@nestjs/common';
export const IS_PUBLIC_KEY = 'isPublic';export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);

This decorator simplifies the declaration of a public endpoint and, by centralizing the metadata key, facilitates future changes.

How to apply the custom decorator on a controller?

Once the Public custom decorator has been created, it can be used in any controller in a simple way:

@Get('new-endpoint')@Public()newEndpoint() { // This endpoint is now public}

The entire authorization flow stays in place, and developers can use the public decorator to exempt specific endpoints from authorization in NestJS applications.

Recommendations for applying guardians and decorators

  • Consistency: Use the custom decorator whenever possible to ensure consistency throughout the codebase.
  • Thorough testing: Before deploying changes, test your endpoints to verify that only those that should be public are public.
  • Documentation: Maintain up-to-date documentation on which endpoints are protected and which are not to avoid security issues.

With custom guardians and decorators, you can efficiently manage the security and accessibility of your endpoints in NestJS. Continue exploring the powerful capabilities of NestJS to create robust and secure applications!

Contributions 3

Questions 0

Sort by:

Want to see more contributions, questions and answers from the community?

Comando para crear decorators:

nest g d auth/decorators/nombre --flat

Lo que hicimos

Esto aplica para ambos proyectos

Para proteger todo un controlador en lugar de solo una ruta en especifico, podemos asignarle el guard al controlador.

// src\app.controller.ts
...
@UseGuards(ApiKeyGuard)
@Controller()
...

Podemos crear un decorador para volver publico alguna ruta protegida.
Para ello, modificamos nuestro controlador

...
const isPublic = this.reflector.get('isPublic', context.getHandler());
    if (isPublic) {
      return true;
    }
const request = context.switchToHttp().getRequest<Request>();
    const authHeader = request.header('Auth');
    return authHeader === '1234';
  }

Creamos un decorador que nos facilite el envió de metadata a la request

// src\auth\decorators\public.decortator.ts
import { SetMetadata } from '@nestjs/common'
export const IS_PUBLIC_KEY = 'isPublic';
export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);

Ahora podemos declara como publico alguna ruta protegida

@Public() // usando el decorador
  @Get('nuevo')
  newEndpoint() {
    return 'yo soy nuevo';
  }

Usando un decorador

Una funcionalidad muy útil a la hora de trabajar con nuestros guardianes es que en vez de nosotros tener que programar cual es la condición que debe cumplir el contexto que necesitamos, lo que podemos hacer es usar decoradores que validen esta información por nosotros:

Para nuestro ejemplo, lo que vamos a hacer es que vamos a proteger todos los Endpoints de nuestro controlador:

  • **src\app.controller.ts**:
// importamos el "SetMetadata"
import { Controller, Get, SetMetadata } from '@nestjs/common';

/* para que todos los enpoints de el controlador esten protegidos colocamos el 
decodador arriba del controlador */
// todos los endpoints estarán protegidos
@UseGuards(ApiKeyGuard)
@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get()
  // SetMetadata nos permite enviar metadatos que podemos recibir en el contexto del guardian
  @SetMetadata('isPublic', true)
  getHello(): string {
    return this.appService.getHello();
  }

  // ...
}

La forma en la que vamos a utilizar este metadato que estamos enviando al contexto del guardián, en este caso va a ser para que con esa configuración protejamos todos los Endpoints del controlador, menos, el Endpoint que tenga el metadato **‘isPiblic’** en **true**.

Veamos como:

  • **src/auth/guards/api-key.guard.ts**:
// importamos al reflector
import { Reflector } from '@nestjs/core';

@Injectable()
export class ApiKeyGuard implements CanActivate {
  // debemos inyectar el reflector en nuestro guardian
  constructor(private reflector: Reflector) {}

  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    // con un método del reflector, nos traemos la metadata de "isPublic"
    // con el contexto, le indicamos que recibimos un manejador
    const isPublic = this.reflector.get('isPublic', context.getHandler());

    // el parámetro tiene la propiedad "isPublic" como verdadera?
    if (isPublic) {
      // no hacemos validación
      return true;
    }

    // si en la metadata, indica que no es publico, hacemos la validación
    // ...
  }
}

De esta forma, si un parámetro tiene en sus metadatos que es publico, no se realizará la validación, pero si en sus metadatos no lo tienen, re debe realizar la validación.

Creando nuestros decoradores con metadatos

El crear nuestros propios decoradores con la metadata que necesitamos es útil para que no caigamos en errores de Typos, y también tanto para agilizar la experiencia de desarrollo, como hacer el código mucho más verboso.

Para realizar esto, vamos a crear este decorador de la siguiente forma:

  • **src\auth\decorators\public.decortator.ts**:
import { SetMetadata } from '@nestjs/common';

// instanciamos como queremos que se va a llamar la metadata
// lo exportamos por si lo queremos reutilizar en otro decodador y en el guardian
export const IS_PUBLIC_KEY = 'isPublic';

// creamos el decorador primero como una función flecha
// va a ser igual al decodador "SetMetadata", pero con el metadato "isPublic" como "true"
export const IsPublic = () => SetMetadata(IS_PUBLIC_KEY, true);

Y ahora solo tendríamos que usar este decorador de la siguiente forma:

  • **src\app.controller.ts**:
// importamos nuestro decoador
import { IsPublic } from './auth/decorators/public.decorator';

@UseGuards(ApiKeyGuard)
@Controller()
export class AppController {

  @Get()
  // lo usamos en el parámetro deseado
  @IsPublic()
  getHello(): string {
    return this.appService.getHello();
  }
}

Y listo, ahora tenemos un decorador un poco más flexible y personalizado.