O JWT (JSON Web Token) se tornou o padrão de autenticação mais usado em APIs modernas. Se você trabalha com backend, frontend ou mobile, entender como JWT funciona é essencial — e debugar tokens faz parte do dia a dia.
Neste guia, vamos destrinchar a estrutura de um JWT, explicar o fluxo de autenticação, mostrar como decodificar tokens e cobrir os erros de segurança mais comuns.
O Que É JWT?
JWT é um padrão aberto (RFC 7519) que define um formato compacto e seguro para transmitir informações entre partes como um objeto JSON. O token é assinado digitalmente, garantindo que seu conteúdo não foi adulterado.
Um JWT tem esta aparência:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6Ik1hcmlhIiwiaWF0IjoxNjE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Estrutura do JWT: As 3 Partes
Um JWT é composto por três partes separadas por pontos (.):
| Parte | Nome | Conteúdo |
|---|---|---|
| 1ª | Header | Algoritmo e tipo do token |
| 2ª | Payload | Claims (dados do usuário/sessão) |
| 3ª | Signature | Assinatura para verificação |
1. Header
Indica o algoritmo de assinatura e o tipo do token:
{
"alg": "HS256",
"typ": "JWT"
}
Os algoritmos mais comuns são:
- HS256 — HMAC com SHA-256 (chave simétrica, mais simples)
- RS256 — RSA com SHA-256 (chave assimétrica, mais seguro para APIs públicas)
- ES256 — ECDSA com SHA-256 (compacto e moderno)
2. Payload (Claims)
Contém as claims — informações sobre o usuário e metadados do token:
{
"sub": "user_123",
"name": "Maria Silva",
"email": "maria@email.com",
"role": "admin",
"iat": 1616239022,
"exp": 1616242622
}
As claims padrão (registradas) mais importantes:
| Claim | Nome | Descrição |
|---|---|---|
sub | Subject | Identificador do usuário |
iat | Issued At | Timestamp de criação |
exp | Expiration | Timestamp de expiração |
iss | Issuer | Quem emitiu o token |
aud | Audience | Para quem o token é destinado |
3. Signature
A assinatura é criada combinando header + payload + secret:
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)
É a assinatura que garante que o token não foi alterado. Se alguém modificar o payload, a assinatura não vai bater e o servidor rejeitará o token.
Fluxo de Autenticação com JWT
O fluxo típico de autenticação funciona assim:
- Login: Usuário envia credenciais (email + senha) para
/api/login - Geração: Servidor valida credenciais e gera um JWT com os dados do usuário
- Armazenamento: Cliente recebe e armazena o token (cookie httpOnly ou localStorage)
- Uso: Em cada request, o cliente envia o token no header
Authorization: Bearer <token> - Verificação: Servidor verifica a assinatura e extrai os dados do token (sem consultar banco)
// Exemplo: enviando JWT em requisições
const response = await fetch('/api/profile', {
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
Como Decodificar um JWT
No Terminal
# Decodificar payload (parte 2)
echo "eyJzdWIiOiIxMjM0NTY3ODkwIn0" | base64 -d
# {"sub":"1234567890"}
Em JavaScript
function decodeJWT(token) {
const parts = token.split('.');
if (parts.length !== 3) throw new Error('Token inválido');
const header = JSON.parse(atob(parts[0]));
const payload = JSON.parse(atob(parts[1]));
return { header, payload };
}
const token = 'eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.xxx';
const decoded = decodeJWT(token);
console.log(decoded.payload.sub); // "1234567890"
Em Python
import jwt # pip install PyJWT
# Decodificar sem verificar assinatura (debug)
payload = jwt.decode(token, options={"verify_signature": False})
print(payload)
# Decodificar COM verificação (produção)
payload = jwt.decode(token, "minha-chave-secreta", algorithms=["HS256"])
print(payload)
Erros Comuns e Vulnerabilidades
1. Armazenar JWT no localStorage
O localStorage é acessível via JavaScript, o que significa que qualquer ataque XSS pode roubar o token. Preferível: armazene em um cookie httpOnly com flags Secure e SameSite.
2. Não validar o algoritmo
O ataque "alg: none" é clássico: o atacante modifica o header para "alg": "none" e remove a assinatura. Sempre valide que o algoritmo é o esperado no servidor.
// ❌ Vulnerável
jwt.verify(token, secret);
// ✅ Seguro — forçar algoritmo
jwt.verify(token, secret, { algorithms: ['HS256'] });
3. Tokens que nunca expiram
Tokens sem exp são um risco. Sempre defina um tempo de expiração curto (15–30 minutos) e use refresh tokens para renovar.
4. Secret fraco
Usar strings como "secret" ou "123456" permite ataques de força bruta. Use chaves geradas com pelo menos 256 bits de entropia:
# Gerar um secret seguro
openssl rand -hex 32
JWT vs. Sessions: Quando Usar Cada Um?
| Critério | JWT (Stateless) | Sessions (Stateful) |
|---|---|---|
| Armazenamento | No cliente | No servidor (Redis, DB) |
| Escalabilidade | Excelente (sem estado) | Requer sessão compartilhada |
| Revogação | Difícil (precisa de blocklist) | Fácil (deletar sessão) |
| SSR / Server-side | Funciona com cookies | Padrão nativo |
| Microsserviços | Ideal | Complexo |
Perguntas Frequentes
JWT é a mesma coisa que OAuth?
Não. OAuth 2.0 é um protocolo de autorização. JWT é um formato de token. O OAuth pode usar JWT como formato dos access tokens, mas são conceitos distintos.
Posso revogar um JWT?
Não diretamente, pois ele é stateless. A prática comum é manter uma blocklist de tokens revogados (ex: em Redis) ou usar tokens de curta duração + refresh tokens.
Devo usar JWT para tudo?
Não. Para apps monolíticos com renderização server-side, sessions tradicionais são mais simples e seguras. JWT brilha em APIs, SPAs e microsserviços.
🛠️ Experimente na prática
Use nossas ferramentas online gratuitas — sem cadastro, direto no navegador.
