O que é autenticação stateless com tokens
Em autenticação baseada em tokens, o back-end não precisa manter estado de sessão por usuário (stateless). Em vez disso, após o login, o servidor emite um token assinado que o cliente passa a enviar em cada requisição. O servidor valida o token a cada chamada e decide se a requisição pode prosseguir.
Na prática, isso reduz dependência de armazenamento de sessão no servidor e facilita cenários com múltiplas instâncias (horizontal scaling). Em contrapartida, exige cuidado com expiração, escopo de dados dentro do token e estratégias de revogação/rotação (tratadas em capítulos específicos).
Fluxo prático com JWT: emissão, envio e validação
1) Emissão do token no login
No login, o cliente envia credenciais (ex.: usuário/senha). Se estiverem corretas, o back-end emite um JWT com claims que identifiquem o usuário e o contexto do token, e devolve ao cliente.
- Entrada: credenciais
- Processamento: autenticar usuário + montar claims + assinar token
- Saída: token (e, opcionalmente, metadados como tempo de expiração)
// Exemplo conceitual de resposta do login (JSON) { "access_token": "<jwt>", "token_type": "Bearer", "expires_in": 900 }2) Envio do token em cada requisição
O cliente deve enviar o token no header HTTP Authorization usando o esquema Bearer:
GET /api/orders HTTP/1.1 Host: api.exemplo.com Authorization: Bearer <jwt>Boas práticas de transporte:
- Ouça o áudio com a tela desligada
- Ganhe Certificado após a conclusão
- + de 5000 cursos para você explorar!
Baixar o aplicativo
- Usar sempre HTTPS (TLS) para evitar interceptação.
- Não enviar o token em query string (evita vazamento em logs, histórico, referer).
- Evitar armazenar tokens em locais facilmente acessíveis por scripts em páginas (risco de XSS); a estratégia de armazenamento depende do tipo de cliente (SPA, mobile, server-side), mas o princípio é minimizar exposição.
3) Validação do token em cada requisição
Em cada chamada protegida, o back-end executa um pipeline típico:
- Extrair o token do header
Authorization. - Verificar formato (Bearer + três partes separadas por ponto).
- Validar assinatura (integridade e autenticidade).
- Validar claims (expiração, emissor, audiência etc.).
- Construir o contexto de segurança (ex.:
req.usercomsube permissões) e seguir para a rota.
// Pseudocódigo de middleware de autenticação function authMiddleware(req, res, next) { const auth = req.headers["authorization"]; if (!auth || !auth.startsWith("Bearer ")) return res.status(401).end(); const token = auth.slice("Bearer ".length); const decoded = verifyJwtSignature(token); // valida assinatura e decodifica validateRegisteredClaims(decoded); // exp, nbf, iss, aud etc. req.user = { id: decoded.sub, roles: decoded.roles }; next(); }Erros comuns na validação:
- Aceitar tokens com algoritmo
noneou não restringir algoritmos aceitos. - Não validar
audeiss(token emitido para outro serviço pode ser aceito indevidamente). - Ignorar
expounbf(aceitar token expirado ou ainda não válido). - Confiar em claims sem validar assinatura (decodificar Base64 não é verificar).
Estrutura do JWT: header, payload e signature
Um JWT é composto por três partes separadas por ponto:
base64url(header).base64url(payload).base64url(signature)Header
Metadados sobre como o token foi assinado (ou criptografado, no caso de JWE). Em JWS (o mais comum), inclui:
alg: algoritmo de assinatura (ex.:HS256,RS256,ES256).typ: geralmenteJWT(opcional).kid: identificador da chave (útil para rotação de chaves).
{ "alg": "RS256", "typ": "JWT", "kid": "key-2026-01" }Payload
Contém as claims (declarações) sobre o sujeito e o contexto do token. Importante: em JWS, o payload é apenas codificado em Base64URL, não é secreto. Qualquer pessoa com o token consegue ler o payload.
{ "sub": "user_123", "iss": "https://auth.exemplo.com", "aud": "api.exemplo.com", "exp": 1760000000, "iat": 1759996400 }Signature
É o resultado da assinatura do header+payload com uma chave. A assinatura garante:
- Integridade: o conteúdo não foi alterado.
- Autenticidade: foi emitido por quem possui a chave de assinatura.
Ela não garante confidencialidade do payload.
Claims padrão (registered claims) e como validar
Claims padrão ajudam interoperabilidade e validações consistentes. As mais usadas em APIs:
| Claim | Significado | Como usar/validar |
|---|---|---|
sub | Subject (identificador do usuário/entidade) | Usar como ID principal do usuário; não colocar PII desnecessária. |
aud | Audience (para quem o token foi emitido) | Validar se corresponde à API/serviço atual. |
iss | Issuer (quem emitiu) | Validar contra a URL/identificador do seu provedor de autenticação. |
exp | Expiration time | Rejeitar se now >= exp (considerar clock skew pequeno). |
iat | Issued at | Útil para auditoria e algumas políticas (ex.: invalidar tokens antes de uma data). |
nbf | Not before | Rejeitar se now < nbf. |
Recomendação prática: defina um conjunto mínimo de validações obrigatórias para qualquer endpoint protegido: assinatura, exp, iss e aud. Use nbf quando fizer sentido (ex.: tokens emitidos para uso futuro).
Claims customizadas: o que colocar (e o que evitar)
Claims customizadas são úteis para reduzir consultas ao banco em cada requisição, mas devem ser usadas com cuidado porque aumentam o tamanho do token e podem ficar desatualizadas até expirar.
Boas opções de claims customizadas
rolesouscope: lista de papéis/escopos para autorização.tenant_id: em sistemas multi-tenant, para reforçar isolamento.amr(método de autenticação) oumfa: sinalizar se houve MFA, quando aplicável.
Evite colocar no token
- Dados sensíveis: senha (óbvio), hashes, tokens de terceiros, segredos, chaves de API.
- PII desnecessária: CPF, endereço, telefone, data de nascimento, e-mail (a menos que seja estritamente necessário e avaliado).
- Dados que mudam com frequência: plano atual, status de assinatura, permissões muito dinâmicas (podem ficar inconsistentes).
Regra prática: coloque no token apenas o necessário para identificar o sujeito e tomar decisões rápidas de autorização; todo o resto deve ser buscado sob demanda ou representado por um identificador.
Assinatura (JWS) vs criptografia (JWE)
JWS: JWT assinado (o mais comum em APIs)
JWS (JSON Web Signature) é quando o token é assinado. Ele protege contra adulteração, mas não esconde o conteúdo. Use JWS quando:
- Você precisa que a API confie que o token foi emitido por um emissor legítimo.
- O payload não contém informações que precisem ser secretas.
- Você quer simplicidade e compatibilidade ampla.
Exemplos de algoritmos:
- HS256: HMAC com segredo compartilhado (mesma chave assina e valida). Exige cuidado com distribuição do segredo entre serviços.
- RS256/ES256: assimétricos (chave privada assina, pública valida). Bom para múltiplos serviços validadores sem compartilhar segredo de assinatura.
JWE: JWT criptografado
JWE (JSON Web Encryption) criptografa o conteúdo do token, fornecendo confidencialidade. Use JWE quando:
- Você precisa transportar dados que não podem ser expostos ao cliente ou a intermediários (mesmo com TLS).
- Há exigências regulatórias/arquiteturais para minimizar exposição de dados no cliente.
Observações importantes:
- JWE aumenta complexidade e custo de processamento.
- Mesmo com JWE, ainda é recomendável minimizar dados sensíveis no token; criptografar não elimina riscos como vazamento do próprio token.
- Em muitos casos, a alternativa mais simples é manter o JWT assinado (JWS) com payload mínimo e buscar dados sensíveis no back-end.
Passo a passo: desenhando um JWT para uma API
Passo 1: Defina o objetivo do token
- Identificar o usuário? (
sub) - Restringir a qual API ele serve? (
aud) - Garantir que veio do emissor correto? (
iss) - Carregar escopos/papéis para autorização? (
scope/roles)
Passo 2: Escolha o tipo de assinatura
- Monólito ou poucos serviços com segredo bem protegido: HS256 pode ser suficiente.
- Múltiplos serviços validando tokens: prefira RS256/ES256 para não distribuir a chave privada.
Passo 3: Monte claims mínimas e consistentes
Exemplo de payload enxuto para API:
{ "iss": "https://auth.exemplo.com", "aud": "api.exemplo.com", "sub": "user_123", "iat": 1760000000, "exp": 1760000900, "nbf": 1760000000, "scope": "orders:read orders:write", "tenant_id": "t_9f3a" }Passo 4: Valide no servidor em todas as rotas protegidas
- Rejeite tokens sem assinatura válida.
- Rejeite tokens com
iss/audinesperados. - Rejeite tokens expirados ou ainda não válidos.
- Normalize o contexto do usuário (ex.: converter
scopeem permissões internas).
Exercícios
Exercício 1: montar um payload adequado
Cenário: uma API api.loja.com recebe tokens emitidos por https://auth.loja.com. O usuário autenticado tem ID u_784, pertence ao tenant tenant_22 e precisa apenas ler pedidos. O token deve durar 15 minutos.
Tarefa: escreva um payload JWT (JSON) contendo claims padrão e customizadas mínimas para esse cenário. Inclua iss, aud, sub, iat, exp e uma claim de escopo/permissão. Use timestamps Unix (segundos) fictícios coerentes (ex.: iat e exp com 900s de diferença).
Exercício 2: identificar riscos de dados sensíveis no token
A seguir, uma lista de claims que alguém propôs colocar no JWT. Para cada item, marque: (A) aceitável, (B) aceitável com ressalvas, (C) não recomendado. Em seguida, justifique em uma frase.
emailcpfpassword_hashrolescredit_card_last4addresstenant_idis_adminrefresh_token(incluir o refresh token dentro do access token)
Dica: lembre que em JWS o payload é legível por quem possui o token, e que tokens podem vazar via logs, extensões, XSS, backups e capturas de tráfego em ambientes não protegidos.