Autenticação JWT: Como Funcionam os JSON Web Tokens
· 12 min de leitura
Índice
- O que é um JSON Web Token?
- Estrutura do JWT Explicada
- Como Funciona a Autenticação JWT
- Tokens de Acesso vs Tokens de Atualização
- Guia de Implementação em Node.js
- Onde Armazenar JWTs com Segurança
- Melhores Práticas de Segurança
- Vulnerabilidades Comuns de JWT
- Escolhendo o Algoritmo Certo
- JWT em Sistemas Distribuídos
- Perguntas Frequentes
- Artigos Relacionados
O que é um JSON Web Token?
Um JSON Web Token (JWT) é uma forma compacta e segura para URL de representar declarações entre duas partes. Os JWTs se tornaram o padrão de fato para autenticação e autorização em aplicações web modernas, APIs e arquiteturas de microsserviços.
Quando um usuário faz login, o servidor gera um JWT contendo informações do usuário e o envia ao cliente. O cliente então inclui este token em requisições subsequentes para provar sua identidade. Pense nisso como um passaporte digital que contém suas credenciais e pode ser verificado sem precisar consultar a autoridade emissora toda vez.
Diferentemente da autenticação tradicional baseada em sessão, onde o servidor mantém o estado da sessão na memória ou em um banco de dados, os JWTs são stateless. Todas as informações necessárias para verificar o token estão contidas no próprio token. Esta decisão arquitetural traz várias vantagens:
- Escalabilidade: Não há necessidade de armazenamento de sessão ou sessões fixas em ambientes com balanceamento de carga
- Autenticação entre domínios: JWTs funcionam perfeitamente em diferentes domínios e serviços
- Amigável para mobile: Perfeito para aplicativos móveis que precisam autenticar com APIs backend
- Microsserviços: Cada serviço pode verificar tokens independentemente sem um armazenamento de sessão central
- Performance: Elimina consultas ao banco de dados para cada requisição autenticada
Você pode inspecionar e decodificar qualquer JWT usando nossa ferramenta de Decodificador JWT para ver seu conteúdo sem precisar escrever nenhum código. Isso é particularmente útil ao depurar problemas de autenticação ou entender quais dados seus tokens contêm.
Dica profissional: JWTs são assinados, não criptografados por padrão. Qualquer pessoa pode decodificar e ler o conteúdo de um JWT. Nunca armazene informações sensíveis como senhas, números de cartão de crédito ou números de seguro social em payloads JWT.
Estrutura do JWT Explicada
Um JWT consiste em três partes distintas separadas por pontos (.): o Cabeçalho, o Payload e a Assinatura. Veja como um JWT real se parece:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiZW1haWwiOiJqb2huQGV4YW1wbGUuY29tIiwicm9sZSI6ImFkbWluIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE1MTYyNDI2MjJ9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Vamos detalhar cada componente para entender como eles trabalham juntos para criar um token seguro e verificável.
1. Cabeçalho
O cabeçalho normalmente contém dois campos críticos que definem como o token deve ser processado:
{
"alg": "HS256",
"typ": "JWT"
}
alg(algoritmo): Especifica o algoritmo criptográfico usado para assinar o tokentyp(tipo): Declara que este é um token JWT
Este objeto JSON é então codificado em Base64Url para formar a primeira parte do JWT. A codificação o torna seguro para URL e compacto para transmissão em cabeçalhos HTTP.
2. Payload
O payload contém declarações — afirmações sobre o usuário e metadados adicionais. É aqui que você armazena os dados reais que deseja transmitir:
{
"sub": "1234567890",
"name": "John Doe",
"email": "[email protected]",
"role": "admin",
"iat": 1516239022,
"exp": 1516242622
}
As declarações são divididas em três categorias:
Declarações registradas (padronizadas e recomendadas):
sub(assunto): Identificador único para o usuárioiat(emitido em): Timestamp de quando o token foi criadoexp(expiração): Timestamp de quando o token expiraiss(emissor): Identifica quem emitiu o tokenaud(audiência): Identifica para quem o token é destinadonbf(não antes): Token não é válido antes deste timestampjti(ID JWT): Identificador único para o próprio token
Declarações públicas são declarações personalizadas que devem ser definidas no Registro IANA de JSON Web Token ou usar nomes resistentes a colisão (como URLs).
Declarações privadas são declarações personalizadas acordadas entre as partes, como role, permissions ou department.
3. Assinatura
A assinatura é o que torna os JWTs seguros e à prova de adulteração. Ela é criada pegando o cabeçalho codificado, o payload codificado, uma chave secreta e aplicando o algoritmo especificado no cabeçalho:
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)
A assinatura serve a dois propósitos críticos:
- Verificação de integridade: Garante que o token não foi modificado desde que foi emitido
- Autenticação: Prova que o token foi criado por alguém com acesso à chave secreta
Se alguém tentar modificar o cabeçalho ou payload, a verificação da assinatura falhará e o token será rejeitado.
Como Funciona a Autenticação JWT
Entender o fluxo completo de autenticação é essencial para implementar JWTs corretamente. Aqui está o processo passo a passo:
Passo 1: Login do Usuário
O usuário envia suas credenciais (nome de usuário e senha) para o endpoint de autenticação. O servidor valida essas credenciais contra o banco de dados.
Passo 2: Geração do Token
Se as credenciais forem válidas, o servidor gera um JWT contendo informações do usuário e declarações. O servidor assina o token usando uma chave secreta (para algoritmos simétricos) ou uma chave privada (para algoritmos assimétricos).
Passo 3: Entrega do Token
O servidor envia o JWT de volta ao cliente, normalmente no corpo da resposta. O cliente armazena este token para requisições futuras.
Passo 4: Requisições Autenticadas
Para requisições subsequentes, o cliente inclui o JWT no cabeçalho Authorization usando o esquema Bearer:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Passo 5: Verificação do Token
O servidor recebe a requisição, extrai o JWT do cabeçalho e verifica a assinatura usando a chave secreta ou pública. Se válido, o servidor extrai as informações do usuário do payload e processa a requisição.
Passo 6: Expiração do Token
Quando o token expira, o cliente deve obter um novo token, seja reautenticando ou usando um token de atualização (coberto na próxima seção).
Dica rápida: Sempre valide a declaração exp no lado do servidor, mesmo que você verifique a expiração no cliente. Verificações do lado do cliente podem ser contornadas, mas a validação do lado do servidor é autoritativa.
Tokens de Acesso vs Tokens de Atualização
Um sistema robusto de autenticação JWT normalmente usa dois tipos de tokens trabalhando juntos: tokens de acesso e tokens de atualização. Entender a distinção é crucial para construir aplicações seguras.
Tokens de Acesso
Tokens de acesso são JWTs de curta duração (normalmente 15 minutos a 1 hora) que concedem acesso a recursos protegidos. Eles contêm identidade do usuário e permissões necessárias para autorizar requisições.
Características:
- Tempo de expiração curto (5-60 minutos)
- Incluído em cada requisição de API
- Contém permissões e papéis do usuário
- Não pode ser revogado antes da expiração (stateless)
Tokens de Atualização
Tokens de atualização são tokens de longa duração (dias a meses) usados exclusivamente para obter novos tokens de acesso. Eles são armazenados com segurança e enviados apenas para o endpoint de atualização de token.
Características:
- Tempo de expiração longo (dias a meses)
- Enviado apenas para o endpoint de atualização
- Pode ser revogado no banco de dados
- Frequentemente armazenado com metadados adicionais (dispositivo, IP, user agent)
Por Que Usar Ambos?
Esta abordagem de token duplo equilibra segurança e experiência do usuário:
| Aspecto | Apenas Token de Acesso | Acesso + Tokens de Atualização |
|---|---|---|
| Segurança | Menor (tokens de longa duração em risco) | Maior (tokens de acesso de curta duração) |
| Experiência do Usuário | Melhor (sem reautenticação) | Melhor (atualização de token sem interrupção) |
| Revogação | Difícil (stateless) | Possível (tokens de atualização no BD) |
| Sobrecarga de Rede | Menor | Ligeiramente maior (requisições de atualização) |
| Complexidade de Implementação | Simples | Moderada |
Fluxo de Atualização de Token
- Cliente detecta que o token de acesso expirou ou está prestes a expirar
- Cliente envia token de atualização para o endpoint
/auth/refresh - Servidor valida token de atualização contra o banco de dados
- Servidor gera novo token de acesso (e opcionalmente novo token de atualização)
- Cliente recebe e armazena novos tokens
- Cliente tenta novamente a requisição original com novo token de acesso
Dica profissional: Implemente rotação de token de atualização — emita um novo token de atualização cada vez que um for usado e invalide o antigo. Isso limita o dano se um token de atualização for comprometido.
Guia de Implementação em Node.js
Vamos construir um sistema completo de autenticação JWT usando Node.js, Express e a biblioteca jsonwebtoken. Esta implementação inclui tokens de acesso e de atualização.
Instalação
npm install express jsonwebtoken bcrypt dotenv
Configuração de Ambiente
Crie um arquivo .env com suas chaves secretas:
ACCESS_TOKEN_SECRET=your-super-secret-access-key-change-this
REFRESH_TOKEN_SECRET=your-super-secret-refresh-key-change-this
ACCESS_TOKEN_EXPIRY=15m
REFRESH_TOKEN_EXPIRY=7d
Geração de Token
const jwt = require('jsonwebtoken');
function generateAccessToken(user) {
return jwt.sign(
{
userId: user.id,
email: user.email,
role: user.role
},
process.env.ACCESS_TOKEN_SECRET,
{ expiresIn: process.env.ACCESS_TOKEN_EXPIRY }
);
}
function generateRefreshToken(user) {
return jwt.sign(
{ userId: user.id },
process.env.REFRESH_TOKEN_SECRET,
{ expiresIn: process.env.REFRESH_TOKEN_EXPIRY }
);
}
Endpoint de Login
const bcrypt = require('bcrypt');
app.post('/auth/login', async (req, res) => {
try {
const { email, password } = req.body;
// Encontrar usuário no banco de dados
const user = await User.findOne({ email });
if (!user) {
return res.status(401).json({ error: 'Credenciais inválidas' });
}
// Verificar senha
const validPassword = await bcrypt.compare(password, user.password);
if (!validPassword) {
return res.status(401).json({ error: 'Credenciais inválidas' });
}
// Gerar tokens
const accessToken = generateAccessToken(user);
const refreshToken = generateRefreshToken(user);
// Armazenar token de atualização no banco de dados
await RefreshToken.create({
token: refreshToken,
userId: user.id,
expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)
});
res.json({
accessToken,
refreshToken,
user: {
id: user.id,
email: user.email,
name: user.name
}
});
} catch (error) {
res.status(500).json({ error: 'Erro no servidor' });
}
});
Middleware de Autenticação
function authenticateToken(req, res, next) {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'Token de acesso necessário' });
}
jwt.verify(token, process.env.ACCESS_TOKEN_SECRET, (err, user) => {
if (err) {
return res.status(403).json({ error: 'Token inválido ou expirado' });
}
req.user = user;
next();
});
}
// Uso
app.get('/api/protected', authenticateToken, (req, res) => {
res.json({
message: 'Dados protegidos',
user: req.user
});
});
Endpoint de Atualização de Token
app.post('/auth/refresh', async (req, res) => {
const { refreshToken } = req.body;
if (!refreshToken) {
return res.status(401).json({ error: 'Token de atualização necessário' });
}
try {
// Verificar se o token de atualização existe no banco de dados
const storedToken = await RefreshToken.findOne({ token: refreshToken });
if (!storedToken) {
return res.status(403).json({ error: 'Token de atualização inválido' });
}
// Verificar assinatura do token
jwt.verify(refreshToken, process.env.REFRESH_TOKEN_SECRET, async (err, user) => {
if (err) {
return res.status(403).json({ error: 'Token de atualização inválido' });
}
// Obter dados do usuário
const userData = await User.findById(user.userId);
// Gerar novo token de acesso
const accessToken = generateAccessToken(userData);
res.json({ accessToken });