Autenticação e Autorização

Autenticação JWT & Roles

Entenda como funciona a autenticação via JWT, sistema de roles e como usar decorators e guards.

Visão Geral

O sistema utiliza JWT (JSON Web Tokens) para autenticação e um sistema de roles para autorização. Todas as rotas são protegidas por padrão, exceto aquelas marcadas com o decorator @Public().

Autenticação (JWT)

  • • Token de acesso (Access Token)
  • • Token de refresh (Refresh Token)
  • • Jitter no TTL (expirações espalhadas)
  • • Validação automática via Guards
  • • Estratégia Passport JWT

Autorização (Roles)

  • • Sistema de roles flexível
  • • Decorators para controle de acesso
  • • Guards para validação de permissões
  • • Múltiplas roles por usuário

Fluxo de Autenticação JWT

1. Login do Usuário

Usuário envia credenciais (email e senha) para o endpoint de login

HTTP RequestPOST /api/v1/auth/login
POST /api/v1/auth/login
Content-Type: application/json

{
  "email": "[email protected]",
  "password": "senha123"
}

2. Validação de Credenciais

O AuthService valida email e senha usando Argon2id para comparar hash da senha

3. Geração de Tokens

Sistema gera dois tokens JWT com TTL base + jitter aleatório:

  • Access Token: base 1h (configurável) + até JWT_JITTER_SECONDS
  • Refresh Token: base 7d (configurável) + o mesmo jitter

HTTP Response200 OK
{
  "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "user": {
    "id": "123",
    "email": "[email protected]",
    "roles": ["admin", "user"]
  }
}

4. Uso do Token

Cliente inclui o Access Token no header Authorization de todas as requisições

HTTP RequestCom Token
GET /api/v1/resources
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json

5. Validação do Token

JwtAuthGuard intercepta a requisição, extrai e valida o token usando a estratégia Passport JWT

6. Refresh Token

Quando o Access Token expira, use o Refresh Token para obter um novo Access Token

HTTP RequestPOST /api/v1/auth/refresh-token
POST /api/v1/auth/refresh-token
Content-Type: application/json

{
  "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

Verificar token

Confira se o Access Token ainda é válido com POST /api/v1/auth/verify-token. Exige o header Authorization: Bearer.

HTTP RequestPOST /api/v1/auth/verify-token
POST /api/v1/auth/verify-token
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
HTTP Response200 OK
{
  "message": "Token is valid",
  "user": {
    "id": "123",
    "email": "[email protected]",
    "roles": ["admin", "user"]
  }
}

Jitter no JWT

Jitter é uma variação aleatória somada ao tempo de expiração (TTL) do token no momento do sign. O objetivo é espalhar o exp entre clientes que logam ao mesmo tempo e reduzir picos de refresh simultâneo (thundering herd).

FórmulaTTL efetivo
exp = now + baseTTL + random(0, JWT_JITTER_SECONDS)

Configuração

  • • Variável: JWT_JITTER_SECONDS
  • • Default: 60 segundos
  • • Aplicado em access e refresh tokens
  • 0 desativa o jitter

Exemplo prático

Com base 1h e jitter 60:

  • • Token A: expira em 3600s (1h + 0s)
  • • Token B: expira em 3637s (1h + 37s)
  • • Token C: expira em 3660s (1h + 60s)

Não é clock skew

Jitter altera só o TTL na emissão do token. A validação do Passport continua com ignoreExpiration: false e sem leeway extra.

Criação de conta

Registre um usuário com POST /api/v1/auth/register. A conta nasce inativa até a confirmação de e-mail; tokens JWT já são retornados (com jitter).

HTTP RequestPOST /api/v1/auth/register
POST /api/v1/auth/register
Content-Type: application/json

{
  "firstName": "Jane",
  "lastName": "Smith",
  "username": "janesmith",
  "email": "[email protected]",
  "password": "Str0ng!P@ssword",
  "confirmPassword": "Str0ng!P@ssword"
}
HTTP Response201 Created
{
  "user": {
    "id": "uuid-...",
    "firstName": "Jane",
    "lastName": "Smith",
    "username": "janesmith",
    "email": "[email protected]",
    "isActive": false,
    "roles": []
  },
  "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "message": "Registration successful. Please check your email to confirm your account."
}

E-mail / username duplicado

Se o e-mail (ou username) já estiver cadastrado, a API responde com 409 Conflict em vez de 400.

HTTP Response409 Conflict
{
  "statusCode": 409,
  "message": "A user with this email already exists",
  "error": "Conflict"
}

Verificação de e-mail

O link/token de verificação chega por SMTP. Em Docker local, abra o MailHog em /mailhog/. Confirme com GET /api/v1/auth/verify-email?token=....

Reset de senha

Fluxo em dois passos: solicitar o e-mail de recuperação e depois definir a nova senha com o token recebido.

1. Solicitar reset

HTTP RequestPOST /api/v1/auth/forgot-password
POST /api/v1/auth/forgot-password
Content-Type: application/json

{
  "email": "[email protected]"
}
HTTP Response200 OK
{
  "message": "Password reset instructions sent to your email"
}

2. Definir nova senha

Use o token do e-mail (em Docker: MailHog em /mailhog/).

HTTP RequestPOST /api/v1/auth/reset-password
POST /api/v1/auth/reset-password
Content-Type: application/json

{
  "token": "abc123def456...",
  "password": "NewStr0ng!P@ssword",
  "confirmPassword": "NewStr0ng!P@ssword"
}
HTTP Response200 OK
{
  "message": "Password reset successfully"
}

Sistema de Roles

O acesso é controlado pelo enum Role. Cada usuário pode ter múltiplas roles; o campo é opcional, mas os valores devem pertencer ao enum.

Valores do enum Role

Estes são os valores definidos em src/modules/auth/enums/role.enum.ts. Para novos papéis, estenda o enum — não use strings livres.

super

Super Administrador - Acesso total ao sistema

admin

Administrador - Gestão de usuários e recursos

manager

Gerente - Gestão de recursos específicos

user

Usuário - Acesso às funcionalidades básicas

Estrutura de roles no modelo

O módulo users tipa roles como Role[]. Veja também a página Users.

TypeScriptRole enum + User
import { Role } from '@modules/auth/enums/role.enum';

enum Role {
  SUPER = 'super',
  ADMIN = 'admin',
  MANAGER = 'manager',
  USER = 'user',
}

interface User {
  id: string;
  email: string;
  roles: Role[];  // Role[] — valores do enum
  // ... other fields
}

// Exemplo de usuário
const user = {
  id: "123",
  email: "[email protected]",
  roles: [Role.ADMIN, Role.USER]  // valores válidos do enum Role
};

Decorators e Guards

Decorators Disponíveis

@Public()- Tornar rota pública

Marca uma rota como pública, permitindo acesso sem autenticação. Útil para endpoints de login, registro, etc.

TypeScriptController
import { Public } from '@modules/auth/decorators/public.decorator';

@Controller('auth')
export class AuthController {
  @Public()  // Rota pública, não requer autenticação
  @Post('login')
  async login(@Body() loginDto: LoginDto) {
    return this.authService.login(loginDto);
  }
}

@Roles()- Restringir por role

Define quais roles são necessárias para acessar a rota. O usuário deve ter pelo menos uma das roles especificadas.

TypeScriptController
import { Roles } from '@modules/auth/decorators/roles.decorator';
import { Role } from '@modules/auth/enums/role.enum';

@Controller('resources')
export class ResourceController {
  @Roles(Role.ADMIN, Role.SUPER)  // valores do enum Role
  @Get()
  async findAll() {
    return this.resourceService.findAll();
  }
  
  @Roles(Role.ADMIN)
  @Delete(':id')
  async delete(@Param('id') id: string) {
    return this.resourceService.delete(id);
  }
}

Guards Implementados

JwtAuthGuard

Valida o token JWT nas requisições: verifica se é válido e não expirado, e disponibiliza o payload em req.user. Rotas com @Public() são liberadas.

RolesGuard

Valida se o usuário autenticado possui ao menos uma das roles exigidas por @Roles().

Exemplos Práticos

Exemplo 1: Rota Pública

TypeScriptPublic Route
@Controller('auth')
export class AuthController {
  @Public()  // Não requer autenticação
  @Post('register')
  async register(@Body() registerDto: RegisterDto) {
    return this.authService.register(registerDto);
  }
}

Exemplo 2: Rota Protegida (Qualquer Usuário Autenticado)

TypeScriptProtected Route
@Controller('resources')
export class ResourceController {
  // Sem @Public() → requer autenticação
  // Sem @Roles() → qualquer usuário autenticado pode acessar
  @Get('me')
  async getMine(@Request() req) {
    return this.resourceService.findByOwner(req.user.id);
  }
}

Exemplo 3: Rota com Role Específica

TypeScriptRole-Based Route
import { Role } from '@modules/auth/enums/role.enum';

@Controller('resources')
export class ResourceController {
  @Roles(Role.SUPER)  // valores do enum Role
  @Get()
  async findAll() {
    return this.resourceService.findAll();
  }
  
  @Roles(Role.ADMIN)
  @Delete(':id')
  async delete(@Param('id') id: string) {
    return this.resourceService.delete(id);
  }
}

Exemplo 4: Múltiplas Roles

TypeScriptMultiple Roles
import { Role } from '@modules/auth/enums/role.enum';

@Controller('resources')
export class ResourceController {
  // Usuário precisa ter Role.ADMIN OU Role.MANAGER (OR)
  @Roles(Role.ADMIN, Role.MANAGER)
  @Get('summary')
  async getSummary() {
    return this.resourceService.getSummary();
  }
  
  // @Roles() usa OR, não AND
  // Para AND, crie um guard customizado
  @Roles(Role.SUPER)
  @Get('audit')
  async getAudit() {
    return this.resourceService.getAudit();
  }
}

Payload do Token

O token JWT carrega o payload abaixo após a autenticação. Os dados ficam disponíveis em req.user.

JSONJWT Payload
{
  "id": "user123",
  "email": "[email protected]",
  "roles": ["admin", "user"],
  "iat": 1234567890,  // Issued at
  "exp": 1234571490   // Expiration
}

Configuração

As configurações de JWT são feitas através de variáveis de ambiente e acessadas via ConfigService.

Environment Variables.env
# JWT Configuration
JWT_SECRET=seu_jwt_secret_super_seguro_aqui
JWT_EXPIRATION_TIME=1h
JWT_REFRESH_SECRET=seu_refresh_secret_super_seguro
JWT_REFRESH_EXPIRATION_TIME=7d
JWT_JITTER_SECONDS=60

# Configuração Argon2id
ARGON2_MEMORY_COST=19456
ARGON2_TIME_COST=2
ARGON2_PARALLELISM=1

Segurança

Use secrets fortes e únicos em produção. O JWT_SECRET deve ser uma string longa e aleatória. Nunca commite secrets no repositório.

Autenticação socialOpcional

Google, Facebook, X / Twitter, GitHub, Figma, LinkedIn, Slack, Atlassian, GitLab, Bitbucket, Discord, Twitch e Reddit OAuth2 usam Authorization Code Exchange — JWTs nunca vão na URL de redirect. Steam usa OpenID 2.0 (não OAuth2) com o mesmo fluxo de exchange code; Steam e Reddit nunca devolvem e-mail. Setup completo, referência da API e exemplos web/mobile estão em Autenticação Social.