Refresh tokens: estratégias seguras de renovação
1. Fundamentos dos Refresh Tokens
Refresh tokens são credenciais de longa duração utilizadas para obter novos access tokens sem exigir que o usuário se autentique novamente. Enquanto o access token (geralmente um JWT) tem validade curta — de 5 a 15 minutos — o refresh token pode durar dias ou semanas.
A principal diferença está no escopo de risco: um access token vaza? O dano é limitado ao seu TTL. Um refresh token vaza? O atacante pode renovar sessões indefinidamente. Por isso, refresh tokens exigem proteção extra.
Cenários típicos:
- APIs REST: servidores trocam refresh tokens por novos access tokens via endpoint dedicado
- SPAs: refresh tokens armazenados em HttpOnly cookies para mitigar XSS
- Mobile apps: refresh tokens protegidos por Secure Enclave (iOS) ou Android Keystore
2. Ciclo de Vida e Rotação Segura
O fluxo padrão de renovação segue este padrão:
POST /api/auth/refresh
Authorization: Bearer <refresh_token>
Resposta 200 OK:
{
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"refresh_token": "dGhpcyBpcyBhIG5ldyByZWZyZXNo...",
"expires_in": 900
}
Rotação automática (token rotation) é a prática de emitir um novo refresh token a cada renovação e invalidar o anterior. Isso limita a janela de exploração caso um token seja roubado.
TTL recomendado:
- Refresh tokens para aplicações web: 7 a 30 dias
- Refresh tokens para mobile: até 90 dias (com possibilidade de renovação silenciosa)
- Refresh tokens para serviços críticos: 24 horas (com reautenticação obrigatória)
3. Armazenamento Seguro do Refresh Token
No Backend
Nunca armazene refresh tokens em texto puro. Faça hash com SHA-256:
// Geração do hash
const crypto = require('crypto');
const hash = crypto.createHash('sha256').update(refreshToken).digest('hex');
// Armazene hash no banco junto com user_id, family_id, expiração
No Frontend (SPA)
| Método | Segurança | Risco |
|---|---|---|
| HttpOnly cookie | Alta | CSRF (mitigável com SameSite=Strict) |
| localStorage | Baixa | XSS pode ler o token |
| sessionStorage | Média | Perde ao fechar aba |
Recomendação: Sempre prefira HttpOnly cookies com atributos Secure, SameSite=Strict e HttpOnly.
Em Mobile
- iOS: Keychain com kSecAttrAccessible = kSecAttrAccessibleWhenUnlockedThisDeviceOnly
- Android: EncryptedSharedPreferences + Android Keystore
4. Proteção Contra Ataques Comuns
Replay Attacks
Cada refresh token deve conter um identificador único (jti - JWT ID). O servidor deve armazenar jtis já utilizados para evitar reuso:
Payload do refresh token:
{
"sub": "user123",
"jti": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"exp": 1700000000,
"family_id": "fam_xyz789"
}
Roubo de Tokens
Implemente fingerprint do cliente — colete User-Agent, IP, device ID e armazene junto ao token. Na renovação, compare os dados:
// Validação de fingerprint
if (storedFingerprint.ip !== currentRequest.ip) {
// Possível roubo: invalida toda a família de tokens
invalidateFamily(token.family_id);
return 403 Forbidden;
}
Binding do Token ao Cliente
Use token binding criptográfico: o refresh token é assinado com uma chave derivada do client_id. Assim, mesmo que vaze, não pode ser usado por outro cliente:
const clientSecret = getClientSecret(clientId);
const bindingKey = crypto.createHmac('sha256', clientSecret)
.update(refreshToken).digest('hex');
// Armazene bindingKey junto ao token no banco
5. Revogação e Blacklist de Refresh Tokens
Blacklist em Cache (Redis)
Use Redis com TTL igual ao tempo restante do token:
// Adicionar à blacklist
SET blacklist:<jti> "revoked" EX <ttl_restante>
// Verificar na renovação
if (redis.exists(`blacklist:${jti}`)) {
return 401 Unauthorized;
}
Revogação por Usuário
Ao alterar senha, invalide todos os tokens do usuário:
// Incrementa versão de segurança do usuário
UPDATE users SET token_version = token_version + 1 WHERE id = $1;
// Na validação do refresh token
if (token.token_version !== user.token_version) {
return 401 "Token revogado por mudança de senha";
}
Revogação por Família
Quando um token é rotacionado, o token pai é invalidado. Se alguém tentar usar o token antigo, detectamos roubo:
// Na rotação
const parentJti = getParentJti(request.refreshToken);
if (redis.exists(`used_jti:${parentJti}`)) {
// Token pai já foi usado — possivelmente roubado
invalidateFamily(token.family_id);
return 403 "Possível roubo de token detectado";
}
redis.set(`used_jti:${parentJti}`, "used", "EX", 86400);
6. Monitoramento e Logging de Renovações
Cada renovação deve gerar um log estruturado:
{
"event": "token_refresh",
"user_id": "user123",
"jti": "a1b2c3d4...",
"family_id": "fam_xyz789",
"timestamp": "2024-01-15T10:30:00Z",
"fingerprint": {
"ip": "192.168.1.100",
"user_agent": "Mozilla/5.0...",
"device_id": "device_abc"
},
"status": "success"
}
Alertas para padrões anômalos:
- Mais de 3 renovações em 1 minuto
- Renovação com fingerprint diferente do original
- Tentativa de uso de jti já utilizado
Auditoria: Mantenha histórico de tokens ativos por usuário para rastreamento de sessões.
7. Considerações de Implementação
Estrutura do Refresh Token
// Payload completo
{
"sub": "user_abc123",
"jti": "uuid-v4",
"exp": 1700100000,
"iat": 1699500000,
"family_id": "fam_001",
"token_version": 3,
"client_id": "web_app_1"
}
Endpoint de Renovação
POST /api/auth/refresh
Content-Type: application/json
Authorization: Bearer <current_refresh_token>
{
"client_id": "web_app_1",
"fingerprint": {
"ip": "192.168.1.100",
"user_agent": "Mozilla/5.0..."
}
}
Respostas:
- 200 OK: { access_token, refresh_token, expires_in }
- 401: Token expirado ou revogado (cliente deve reautenticar)
- 403: Roubo suspeito (invalida família, cliente deve reautenticar)
Tratamento de Erros
| Código | Significado | Ação do Cliente |
|---|---|---|
| 401 | Token expirado ou revogado | Reautenticar |
| 403 | Roubo detectado | Reautenticar + alerta de segurança |
| 429 | Muitas requisições | Aguardar e tentar novamente |
Referências
- RFC 6749 - The OAuth 2.0 Authorization Framework — Especificação oficial do OAuth 2.0 que define refresh tokens e seu fluxo
- OAuth 2.0 for Browser-Based Applications (Best Current Practice) — Diretrizes de segurança para SPAs com refresh tokens
- Auth0 - Refresh Token Rotation — Guia prático de implementação de rotação automática de refresh tokens
- OWASP - JSON Web Token Cheat Sheet — Boas práticas de segurança para JWTs, incluindo refresh tokens
- IETF - OAuth 2.0 Token Binding — Especificação sobre binding criptográfico de tokens ao cliente
- Redis - Token Blacklist Patterns — Estratégias de revogação de tokens usando Redis
- Okta - The Complete Guide to Refresh Tokens — Guia completo sobre refresh tokens com exemplos práticos