Implementando Controle de Acesso com Passport e JWT no NestJS

    felipebdn
    há 6 meses
    0

    Neste exemplo, vamos explorar como implementar um sistema de controle de acesso no NestJS usando o Passport para autenticação e JSON Web Tokens (JWT) para autorização.

    Pré-requisitos

    Certifique-se de ter o NestJS instalado e uma compreensão básica do Passport, JWT e da estrutura de projetos NestJS.

    Passo 1: Configurando o Passport e a Estratégia JWT JwtStrategy

    O Passport é uma estrutura de autenticação extensível que integra diversas estratégias. A estratégia JWT é particularmente útil para autenticar usuários por meio de tokens JWT. Aqui está como configuramos a estratégia:

    // jwt.strategy.ts import { Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { PassportStrategy } from '@nestjs/passport'; import { ExtractJwt, Strategy } from 'passport-jwt'; import { z } from 'zod'; import { Env } from '../env'; const userPayloadSchema = z.object({ sub: z.string().uuid(), role: z.enum(['MEMBER', 'ADMIN']), }); export type UserPayload = z.infer<typeof userPayloadSchema>; @Injectable() export class JwtStrategy extends PassportStrategy(Strategy) { constructor(private config: ConfigService<Env, true>) { const publicKey = config.get('JWT_PUBLIC_KEY', { infer: true }); super({ jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), secretOrKey: Buffer.from(publicKey, 'base64'), algorithms: ['RS256'], }); } async validate(payload: UserPayload) { return userPayloadSchema.parse(payload); } }

    Aqui, definimos uma estratégia JWT, especificando como o Passport deve extrair o token da requisição e qual chave secreta usar para verificar a assinatura do token. O método validate é chamado para verificar a validade do usuário com base nos dados do payload do token.

    Passo 2: Guarda de Funções RolesGuard

    A guarda guard de funções é responsável por verificar se o usuário tem as funções necessárias para acessar uma rota específica. Aqui está a implementação:

    // roles.guard.ts import { Injectable, CanActivate, ExecutionContext, UnauthorizedException } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; @Injectable() export class RolesGuard implements CanActivate { constructor(private reflector: Reflector) {} canActivate(context: ExecutionContext): boolean { const roles = this.reflector.get<string[]>('roles', context.getHandler()); if (!roles) { return true; // Se não houver restrições de função, permita o acesso (membros padrão) } const request = context.switchToHttp().getRequest(); const user = request.user; if (!user || !user.role) { throw new UnauthorizedException('Acesso não autorizado.'); } return roles.includes(user.role); } }

    Essa guarda examina os metadados metadata associados a uma rota usando o Reflector do NestJS. Se não houver restrições de função, a rota é acessível. Caso contrário, verifica se o usuário possui a função necessária.

    Passo 3: Decoradores para Definir Funções Roles, Admin

    Os decoradores são utilizados para designar as funções necessárias para acessar uma rota específica. Veja como eles são implementados:

    // roles.decorator.ts import { SetMetadata } from '@nestjs/common'; export const Roles = (...roles: string[]) => SetMetadata('roles', roles); export const Admin = () => Roles('ADMIN');

    Esses decoradores são utilizados para marcar controladores ou rotas específicas com as funções permitidas. O decorador Admin é uma forma específica de Roles para simplificar a marcação de rotas de administrador.

    Passo 4: Uso nos Controladores

    Por fim, aplicamos esses conceitos nos controladores. Aqui está um exemplo de como você pode usar as guardas e decoradores:

    // seu.controller.ts import { Controller, Get, UseGuards } from '@nestjs/common'; import { JwtAuthGuard } from './jwt-auth.guard'; import { RolesGuard, Admin } from './roles.guard'; @UseGuards(JwtAuthGuard, RolesGuard) @Controller('seu-controller') export class SeuController { @Get('rota-comum') rotaComum() { return 'Esta rota é acessível por membros.'; } @Get('rota-de-admin') @Admin() rotaDeAdmin() { return 'Esta rota é acessível apenas por administradores.'; } }

    Neste exemplo, aplicamos as guardas JwtAuthGuard e RolesGuard ao controlador usando o decorador @UseGuards. Isso garante que todas as rotas dentro desse controlador exijam autenticação e verificação de funções. Na rota rota-comum, qualquer usuário autenticado pode acessar, pois não especificamos funções adicionais. Já na rota rota-de-admin, usamos o decorador @Admin(), que é essencialmente um atalho para @Roles('ADMIN'). Isso garante que apenas usuários com a função de administrador possam acessar essa rota.

    Conclusão

    Ao integrar o Passport e os JWTs no NestJS, conseguimos criar um sistema de controle de acesso robusto. O Passport cuida da autenticação, enquanto as guardas e decoradores fornecem uma maneira elegante de controlar quem pode acessar quais partes do aplicativo.

    Espero que esse guia tenha sido útil para entender como implementar controle de acesso em seu aplicativo NestJS. Lembre-se de adaptar esses conceitos de acordo com as necessidades específicas do seu projeto.