O que e Autenticacao JWT?
JSON Web Token (JWT, pronunciado "jot") e um padrao compacto e autocontido para transmitir informacoes de autenticacao e autorizacao entre partes como um token assinado digitalmente. Uma vez que o usuario faz login, o servidor emite um JWT contendo claims sobre aquele usuario. O cliente armazena o token e o envia com cada requisicao subsequente. O servidor verifica a assinatura do token para confirmar a autenticidade — sem necessidade de consulta ao banco de dados.
JWTs sao definidos pela RFC 7519 e sao o mecanismo de autenticacao dominante para APIs REST, aplicacoes single-page e arquiteturas de microsservicos. Sao stateless por design: o servidor nao precisa armazenar dados de sessao, tornando a escalabilidade horizontal simples.
Como JWT Funciona
O fluxo completo de autenticacao JWT funciona assim:
- Login: O usuario envia suas credenciais (usuario + senha) para o endpoint de login do servidor.
- Emissao do token: O servidor valida as credenciais no banco de dados. Em caso de sucesso, assina um JWT contendo o ID do usuario, roles e um timestamp de expiracao, e retorna o token ao cliente.
- Armazenamento do token: O cliente armazena o token (em memoria, localStorage ou um cookie httpOnly — mais sobre isso adiante).
- Requisicoes autenticadas: Para cada requisicao API subsequente, o cliente inclui o JWT no header
Authorization:Authorization: Bearer <token>. - Verificacao: O servidor extrai o token do header, verifica a assinatura usando sua chave secreta, verifica a expiracao e le os claims — tudo sem consulta ao banco de dados.
- Resposta: Se o token for valido, o servidor processa a requisicao. Se nao (expirado, adulterado, ausente), retorna
401 Unauthorized.
Estrutura do JWT Explicada
Um JWT consiste em tres partes codificadas em Base64URL separadas por pontos:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiIxMjM0NTYiLCJlbWFpbCI6InVzZXJAZXhhbXBsZS5jb20iLCJyb2xlIjoiYWRtaW4iLCJpYXQiOjE3MDkzMDAwMDAsImV4cCI6MTcwOTMwMzYwMH0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
As tres partes sao:
1. Header
{
"alg": "HS256", // Algoritmo de assinatura (HS256, RS256, ES256)
"typ": "JWT" // Tipo do token
}
2. Payload (Claims)
{
"userId": "123456",
"email": "user@example.com",
"role": "admin",
"iat": 1709300000, // Emitido em (Unix timestamp)
"exp": 1709303600 // Expiracao (1 hora apos emissao)
}
Nomes de claims padroes: iss (emissor), sub (assunto), aud (audiencia), exp (expiracao), iat (emitido em), jti (ID JWT para revogacao). O payload e codificado em Base64URL, nao criptografado — qualquer pessoa pode decodifica-lo. Nunca armazene senhas, numeros de cartao de credito ou outros dados sensiveis no payload.
3. Assinatura
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secretKey
)
A assinatura prova que o token foi emitido pelo seu servidor e nao foi adulterado. Se qualquer caractere no header ou payload mudar, a verificacao da assinatura falha.
Implementando JWT no Node.js
Instale o pacote jsonwebtoken:
npm install jsonwebtoken bcryptjs express
Crie um exemplo completo de login e rota protegida:
// auth.js
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');
const express = require('express');
const router = express.Router();
const JWT_SECRET = process.env.JWT_SECRET; // Deve ser uma string longa e aleatoria
const JWT_EXPIRES_IN = '1h'; // Mantenha access tokens com vida curta
// Rota de login — emite um JWT em autenticacao bem-sucedida
router.post('/login', async (req, res) => {
const { email, password } = req.body;
// 1. Buscar o usuario no banco de dados
const user = await User.findOne({ email });
if (!user) {
return res.status(401).json({ error: 'Invalid credentials' });
}
// 2. Comparar senha com hash armazenado
const validPassword = await bcrypt.compare(password, user.passwordHash);
if (!validPassword) {
return res.status(401).json({ error: 'Invalid credentials' });
}
// 3. Assinar o JWT — inclua apenas claims nao sensiveis
const token = jwt.sign(
{ userId: user._id, email: user.email, role: user.role },
JWT_SECRET,
{ expiresIn: JWT_EXPIRES_IN, algorithm: 'HS256' }
);
res.json({ token, expiresIn: 3600 });
});
// Middleware para verificar JWT em rotas protegidas
function authenticateToken(req, res, next) {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1]; // "Bearer TOKEN"
if (!token) {
return res.status(401).json({ error: 'Access token required' });
}
try {
// Especifique explicitamente o algoritmo para prevenir ataques de confusao de algoritmo
const decoded = jwt.verify(token, JWT_SECRET, { algorithms: ['HS256'] });
req.user = decoded;
next();
} catch (err) {
if (err.name === 'TokenExpiredError') {
return res.status(401).json({ error: 'Token expired' });
}
return res.status(403).json({ error: 'Invalid token' });
}
}
// Exemplo de rota protegida
router.get('/profile', authenticateToken, (req, res) => {
res.json({ userId: req.user.userId, email: req.user.email });
});
module.exports = { router, authenticateToken };
Armazenando JWT no React
Onde voce armazena o JWT no navegador afeta significativamente a postura de seguranca da sua aplicacao. As duas opcoes principais sao localStorage e cookies httpOnly:
| Propriedade | localStorage | Cookie httpOnly |
|---|---|---|
| Vulnerabilidade XSS | Alta — qualquer script pode le-lo | Baixa — inacessivel ao JavaScript |
| Vulnerabilidade CSRF | Nenhuma | Sim — mitigar com SameSite + token CSRF |
| Enviado automaticamente | Nao — deve adicionar ao header manualmente | Sim — navegador envia com cada requisicao |
| Acessivel via JS | Sim | Nao (flag httpOnly) |
| Recomendado para | Apps de baixo risco, dados publicos | Apps em producao com dados de usuario |
Para aplicacoes em producao, prefira cookies httpOnly. Seu servidor define o cookie no login e o navegador o envia automaticamente, sem o JavaScript nunca tocar no valor do token.
Se voce usar localStorage (por ex., por simplicidade durante o desenvolvimento), armazene o token em memoria quando possivel e implemente uma Content Security Policy robusta para mitigar riscos de XSS:
// React — enviando JWT do localStorage
async function fetchProfile() {
const token = localStorage.getItem('authToken');
const response = await fetch('/api/profile', {
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
},
});
if (response.status === 401) {
// Token expirado — redirecionar para login ou renovar
localStorage.removeItem('authToken');
window.location.href = '/login';
}
return response.json();
}
Estrategia de Refresh Token
Access tokens com vida curta (15 minutos a 1 hora) minimizam a janela de roubo de token, mas forcar usuarios a fazer login a cada hora e uma experiencia terrivel. A solucao e um par de refresh token:
- Access token: Vida curta (15 min – 1 hora), enviado com cada requisicao API no header
Authorization. Armazenado em memoria ou localStorage. - Refresh token: Vida longa (7 – 30 dias), usado apenas para obter novos access tokens. Sempre armazenado em um cookie httpOnly, Secure, SameSite=Strict. Deve ser rotacionado a cada uso.
// Servidor: emitir ambos os tokens no login
router.post('/login', async (req, res) => {
// ... validar credenciais ...
const accessToken = jwt.sign(
{ userId: user._id, role: user.role },
process.env.JWT_ACCESS_SECRET,
{ expiresIn: '15m' }
);
const refreshToken = jwt.sign(
{ userId: user._id },
process.env.JWT_REFRESH_SECRET,
{ expiresIn: '30d' }
);
// Armazenar hash do refresh token no banco para suporte a revogacao
await RefreshToken.create({ userId: user._id, token: hashToken(refreshToken) });
// Enviar refresh token como cookie httpOnly
res.cookie('refreshToken', refreshToken, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'strict',
maxAge: 30 * 24 * 60 * 60 * 1000, // 30 dias em ms
});
res.json({ accessToken });
});
// Servidor: endpoint de refresh
router.post('/refresh', async (req, res) => {
const refreshToken = req.cookies.refreshToken;
if (!refreshToken) return res.status(401).json({ error: 'No refresh token' });
try {
const decoded = jwt.verify(refreshToken, process.env.JWT_REFRESH_SECRET);
// Opcional: verificar se hash do token existe no BD (habilita revogacao)
const newAccessToken = jwt.sign(
{ userId: decoded.userId },
process.env.JWT_ACCESS_SECRET,
{ expiresIn: '15m' }
);
res.json({ accessToken: newAccessToken });
} catch {
res.status(403).json({ error: 'Invalid refresh token' });
}
});
Melhores Praticas de Seguranca JWT
- Mantenha access tokens com vida curta: 15 minutos a 1 hora. Tokens roubados ficam inuteis apos a expiracao.
- Use secrets fortes: Seu
JWT_SECRETdeve ter pelo menos 256 bits de dados aleatorios. Gere comopenssl rand -hex 32. Nunca o coloque hardcoded. - Sempre especifique o algoritmo: Passe
{ algorithms: ['HS256'] }parajwt.verify(). Isso previne o ataque de confusao de algoritmo (none) onde um atacante define"alg": "none"no header. - Apenas HTTPS em producao: JWTs em transito sao tao seguros quanto sua camada de transporte. Sempre use TLS em producao.
- Armazene refresh tokens em cookies httpOnly: Nao em localStorage ou sessionStorage.
- Implemente revogacao de token: Para logout e eventos de seguranca, mantenha uma blocklist de curta duracao (Redis funciona bem) ou use refresh tokens rotativos com rastreamento no banco de dados.
- Valide todos os claims: Verifique
exp(expiracao),iss(emissor) eaud(audiencia) em cada requisicao. A bibliotecajsonwebtokenfaz isso automaticamente quando voce passa as opcoes corretas.
Erros Comuns com JWT
Armazenar JWTs no localStorage em apps de alto risco
Qualquer vulnerabilidade XSS — mesmo um script de terceiros que voce carregou — pode ler localStorage e exfiltrar todos os tokens. Para aplicacoes que lidam com pagamentos, dados pessoais ou acoes administrativas, use cookies httpOnly exclusivamente.
Sem expiracao nos tokens
Tokens sem claim exp sao validos para sempre. Um token vazado se torna uma backdoor permanente. Sempre defina expiresIn ao assinar.
O ataque algorithm none
Bibliotecas JWT antigas aceitavam "alg": "none" no header, efetivamente aceitando tokens sem assinatura como validos. Sempre passe a opcao algorithms para jwt.verify() para restringir quais algoritmos sao aceitaveis:
// Sempre faca isso — nunca permita o algoritmo "none"
jwt.verify(token, secret, { algorithms: ['HS256'] });
Dados sensiveis no payload
O payload JWT e codificado em Base64URL, nao criptografado. Qualquer parte com o token pode decodificar e ler o payload sem conhecer o secret. Nunca inclua senhas, CPFs, numeros de cartao de credito ou detalhes internos do sistema nos claims JWT.
Usar o mesmo secret para access e refresh tokens
Se voce usa o mesmo secret para ambos os tipos de token, um access token comprometido poderia potencialmente ser usado como refresh token. Use secrets separados para cada tipo de token armazenados em variaveis de ambiente separadas.
Decodifique Seus Tokens JWT
Precisa inspecionar um JWT para verificar seus claims, expiracao ou estrutura? Cole-o no Decodificador JWT do devbit.dev para decodificar instantaneamente o header e payload, verificar o tempo de expiracao e validar o formato do token — tudo no navegador sem dados enviados a nenhum servidor.
Decodifique e Inspecione Qualquer JWT Instantaneamente
Cole um token JWT para decodificar seu header, payload e claims. Verifica expiracao, algoritmo e estrutura. 100% client-side — seu token nunca sai do navegador.
Abrir Decodificador JWT →Ferramentas Relacionadas
- Decodificador e Inspetor JWT — decodifique qualquer JSON Web Token instantaneamente, verifique expiracao e claims
- Debugger de Erro CORS — corrija erros CORS com o header Authorization no Express, Nginx
- Gerador de Hash — gere hashes SHA-256, MD5 e bcrypt para senhas e tokens
- Codigos de Status HTTP — entenda 401 Unauthorized vs 403 Forbidden e outros codigos de autenticacao
- Inspetor ENV — valide JWT_SECRET e outras variaveis de ambiente no seu arquivo .env
- Ver todas as ferramentas gratuitas