Comando para crear decorators:
nest g d auth/decorators/nombre --flat
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?
Create an account or log in
Keep learning for free! Join and start boosting your career
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.
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.
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.
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 } }
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.
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.
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.
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
Comando para crear decorators:
nest g d auth/decorators/nombre --flat
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';
}
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.
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.
Want to see more contributions, questions and answers from the community?