Extensión de Guardianes JWT en NestJS para Endpoints Públicos

Clase 14 de 22Curso de NestJS: Autenticación con Passport y JWT

Resumen

¿Cómo proteger los endpoints de un controlador con un JSON Web Token?

Proteger los endpoints de un controlador mediante un JSON Web Token (JWT) es una práctica esencial para garantizar la seguridad de una aplicación eCommerce o cualquier API. Los procesos de obtener, crear, actualizar y eliminar productos deben estar resguardados bajo un sistema de autorización que verifique la autenticidad y permisos del usuario.

NestJS facilita este proceso con la ayuda del paquete "passport" que permite implementar estrategias de autenticación, como JWT. Sin embargo, pese a que la mayoría de los endpoints deben estar protegidos, hay ocasiones, como en el caso del endpoint para obtener productos, que necesitamos que sea accesible públicamente.

Configuración de un guardián personalizado

Para implementar esta funcionalidad, creamos un guardián (también conocido como "guard") a través de NestJS que verifica JWT. Aquí explicamos cómo personalizarlo:

  1. Creación del guardián: Iniciar con el siguiente comando en la terminal del proyecto NestJS:

    nest generate guard auth/jwt-auth
    

    Esto generará un archivo base para nuestro nuevo guardián.

  2. Extender las funcionalidades de JWT: En lugar de reescribir la función canActivate, podemos expandirla.

    import { Injectable, ExecutionContext } from '@nestjs/common';
    import { AuthGuard } from '@nestjs/passport';
    
    @Injectable()
    export class JwtAuthGuard extends AuthGuard('jwt') {
      canActivate(context: ExecutionContext) {
        return super.canActivate(context);  // Mantiene la funcionalidad original
      }
    }
    

    Sin embargo, necesitamos verificar si el acceso debe ser público.

  3. Lectura de metadata: Utilizamos el decorador @Public() para aquellos endpoints que deben ser accesibles sin autenticación.

    // Incorporando Reflector para manejar metadata
    import { Reflector } from '@nestjs/core';
    
    constructor(private reflector: Reflector) {}
    
    canActivate(context: ExecutionContext) {
      const isPublic = this.reflector.getAllAndOverride<boolean>('isPublic', [
        context.getHandler(),
        context.getClass(),
      ]);
      if (isPublic) {
        return true;  // Si el endpoint es público, permitir acceso
      }
      return super.canActivate(context); // De lo contrario, validar el JWT
    }
    

Implementación del decorador @Public

Para que un endpoint sea accesible sin necesidad de un token, creamos un decorador personalizado:

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

export const IS_PUBLIC_KEY = 'isPublic';
export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);

Esto permite anotar métodos específicos del controlador para indicar que no requieren autenticación, como el siguiente ejemplo:

@ApiTags('products')
@Controller('products')
export class ProductsController {
  @Public()
  @Get()
  findAll(): string {
    return 'This action returns all products';
  }
}

Prueba del entorno

Con la implementación de este guardián personalizado, puedes verificar su efecto directamente en Insomnia o Postman. Intenta realizar una solicitud GET a tu endpoint público para obtener productos; debe ser accesible sin necesidad de autenticarlo con un token.

Ventajas y consejos prácticos

  • Flexibilidad: Esta aproximación permite seleccionar qué endpoints son públicos o privados mediante decoradores sencillos, manteniendo un código limpio y organizado.

  • Reutilización: Esta funcionalidad puede ser aplicable a otros proyectos gracias a la arquitectura modular de NestJS.

  • Seguridad: Asegúrate de usar tokens seguros y de realizar revisiones periódicas sobre los permisos de acceso de cada endpoint.

Mantener una API segura y eficiente es crucial para proteger los datos de usuario y la integridad de la aplicación. Estos métodos garantizan que los usuarios sólo accedan a los recursos para los cuales están autorizados, al mismo tiempo que permite un acceso público controlado cuando sea necesario. ¡Continúa explorando y aplicando estas prácticas en tus proyectos para sacar el máximo provecho de NestJS!