📚 Módulo 05: Backend - Autenticação e Autorização (JWT)

A nossa API consegue faturar pedidos, mas as rotas de criação e exclusão de produtos não estão protegidas. Se qualquer usuário mal-intencionado descobrir a rota POST /produtos, ele adicionará itens fictícios no nosso banco.

Implementaremos a camada de Segurança do NestJS utilizando passport-jwt e Guards.


🔐 1. O Fluxo de Autenticação JWT

O NestJS possui uma proteção nativa chamada Guards (@UseGuards), que age interceptando a requisição antes mesmo dela chegar ao Controlador.

flowchart TD
    A[BFF do Next.js] -->|1. POST /auth/login| B(AuthService)
    B -->|2. Valida Senha (Bcrypt)| C[(PostgreSQL)]
    B -->|3. Assina JWT (Secret)| D[Retorna Token]
    D --> A

    A -->|4. POST /produtos + Header Bearer| E{JwtAuthGuard}
    E -- Token Válido --> F{RolesGuard Admin?}
    E -- Token Falso/Expirado --> G[HTTP 401 Unauthorized]
    F -- Não é Admin --> H[HTTP 403 Forbidden]
    F -- É Admin --> I[ProdutoController]

Instalação de Dependências

npm install @nestjs/jwt @nestjs/passport passport passport-jwt bcrypt
npm install @types/passport-jwt @types/bcrypt --save-dev

Gerando os módulos:

npx nest g module auth
npx nest g controller auth
npx nest g service auth

🔑 2. Módulo de Autenticação

src/auth/auth.module.ts

Configure o JWT globalmente. Em produção, você guardaria a “secret” no arquivo .env!

import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { JwtStrategy } from './jwt.strategy';

@Module({
  imports: [
    PassportModule,
    JwtModule.register({
      secret: 'MINHA_CHAVE_SUPER_SECRETA_TECLOJA_2024',
      signOptions: { expiresIn: '1d' }, // Expira em 1 dia
    }),
  ],
  controllers: [AuthController],
  providers: [AuthService, JwtStrategy],
})
export class AuthModule {}

O Serviço de Validação (auth.service.ts)

import { Injectable, UnauthorizedException } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { JwtService } from '@nestjs/jwt';
import { LoginDto } from './dto/login.dto';
import * as bcrypt from 'bcrypt';

@Injectable()
export class AuthService {
  constructor(
    private prisma: PrismaService,
    private jwtService: JwtService
  ) {}

  async login(loginDto: LoginDto) {
    const usuario = await this.prisma.usuario.findUnique({
      where: { email: loginDto.email }
    });

    if (!usuario) throw new UnauthorizedException('Credenciais Inválidas');

    // Valida o Hash da Senha gravada no banco
    const senhaValida = await bcrypt.compare(loginDto.senha, usuario.senha);
    if (!senhaValida) throw new UnauthorizedException('Credenciais Inválidas');

    // Prepara as informações a serem carimbadas no Payload do Token (O papel é essencial!)
    const payload = { sub: usuario.id, email: usuario.email, role: usuario.role };

    return {
      usuario: { id: usuario.id, nome: usuario.nome, role: usuario.role },
      token: this.jwtService.sign(payload)
    };
  }
}

(Nota: Crie um LoginDto na pasta /dto/ contendo email e senha com class-validator como fizemos no Módulo 03).


🛡️ 3. Estratégia JWT e Guards de Autorização

O Guard diz: “Você tem a credencial certa”. A Autorização diz: “A credencial é verdadeira, mas você NÃO tem permissão administrativa.”

A Estratégia de Descriptografia (jwt.strategy.ts)

Este arquivo é acionado pela biblioteca Passport toda vez que o @UseGuards(JwtAuthGuard) é chamado.

// src/auth/jwt.strategy.ts
import { ExtractJwt, Strategy } from 'passport-jwt';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable } from '@nestjs/common';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor() {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      ignoreExpiration: false,
      secretOrKey: 'MINHA_CHAVE_SUPER_SECRETA_TECLOJA_2024',
    });
  }

  async validate(payload: any) {
    // O retorno deste método injeta os dados no objeto request.user do Express!
    return { userId: payload.sub, email: payload.email, role: payload.role };
  }
}

O Role Guard customizado (roles.guard.ts)

// src/auth/roles.guard.ts
import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from '@nestjs/common';

@Injectable()
export class RolesGuard implements CanActivate {
  canActivate(context: ExecutionContext): boolean {
    const request = context.switchToHttp().getRequest();
    const user = request.user; // Populado pelo JwtStrategy

    if (!user || user.role !== 'ADMIN') {
      throw new ForbiddenException('Acesso negado. Requer privilégios de Administrador.');
    }

    return true;
  }
}

🛑 4. Trancando as Rotas Administrativas

Agora, voltaremos ao nosso ProdutoController para aplicar a segurança nas rotas de criação:

// src/produto/produto.controller.ts
import { Controller, Post, UseGuards /* imports... */ } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { RolesGuard } from '../auth/roles.guard';

@Controller('produtos')
export class ProdutoController {
  
  @Post()
  @UseGuards(AuthGuard('jwt'), RolesGuard) // Proteção Dupla!
  create(@Body() createProdutoDto: CreateProdutoDto) {
    return this.produtoService.create(createProdutoDto);
  }

  // O GET é público (qualquer usuário não-logado pode ver a vitrine)
  @Get()
  findAll() {
    return this.produtoService.findAll();
  }
}

✅ Pré-Requisitos deste Módulo

Antes de passar para o desenvolvimento do frontend SSR com Next.js no Módulo 06, certifique-se de que:


🤔 Por que fizemos assim?


🔍 Checkpoint

  1. Geração de Token: Dispare uma requisição do tipo POST para /auth/login informando o e-mail semeado admin@tecloja.com e a senha admin123. A resposta deve conter o objeto de usuário e a string do token JWT.
  2. Proteção de Endpoint: Tente disparar um POST em /produtos sem passar cabeçalhos. A API deve rejeitar a requisição com o código de erro 401 Unauthorized.
  3. Bloqueio de privilégios: Autentique-se com um usuário comum semeado (USER) e tente usar o token recebido no cabeçalho Authorization: Bearer <token> para criar um produto. A API deve retornar 403 Forbidden sinalizando falta de privilégios.

⚠️ Erros Comuns

Erro Causa Solução
O objeto request.user chega como nulo ou indefinido dentro do RolesGuard Os guards foram declarados de forma invertida ou o AuthGuard não foi fornecido na rota administrativa. Certifique-se de declarar sempre o guard de autenticação antes do guard de privilégios na anotação: @UseGuards(AuthGuard('jwt'), RolesGuard).
Assinaturas JWT sendo rejeitadas como inválidas pelo validador A chave secreta de criptografia declarada em AuthModule difere da secret configurada no construtor de JwtStrategy. Verifique detalhadamente se a string secreta (ou a variável de ambiente JWT_SECRET) bate exatamente nas duas classes configuradas.
O build do Docker do backend falha ao compilar a dependência do bcrypt A imagem Alpine leve do Node não possui o compilador nativo (g++) necessário para buildar os binários nativos de C++ do bcrypt. Instale a versão puramente JavaScript do Bcrypt (npm install bcryptjs e @types/bcryptjs para desenvolvimento) para evitar gargalos de compilação em containers Docker leves de produção.

🏁 Conclusão (Fim da API Backend)

Parabéns! Nosso servidor NestJS está completo, seguro e performático. Ele está pronto para receber requisições de clientes Web, Mobile ou Desktop.

No Módulo 06, faremos a grande virada arquitetônica. Entraremos no repositório de Frontend (SPA/Vite) e substituí-lo-emos inteiramente pelo ecossistema majestoso do Next.js.


Voltar para o Sumário