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
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
{
"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
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
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.
POST /api/v1/auth/verify-token Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... Content-Type: application/json
{
"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).
exp = now + baseTTL + random(0, JWT_JITTER_SECONDS)
Configuração
- • Variável:
JWT_JITTER_SECONDS - • Default: 60 segundos
- • Aplicado em access e refresh tokens
- •
0desativa 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).
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"
}
{
"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.
{
"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
POST /api/v1/auth/forgot-password
Content-Type: application/json
{
"email": "[email protected]"
}
{
"message": "Password reset instructions sent to your email"
}
2. Definir nova senha
Use o token do e-mail (em Docker: MailHog em /mailhog/).
POST /api/v1/auth/reset-password
Content-Type: application/json
{
"token": "abc123def456...",
"password": "NewStr0ng!P@ssword",
"confirmPassword": "NewStr0ng!P@ssword"
}
{
"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 Administrador - Acesso total ao sistema
Administrador - Gestão de usuários e recursos
Gerente - Gestão de recursos específicos
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.
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.
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.
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
@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)
@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
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
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.
{
"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.
# 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.