JWT, Paseto ou sessions: quando cada um
⏱ 12 min·⭐ 55 XP
Pré-requisitos (0/1)0%
- ⬜🔐 OAuth2 e OIDC do zero: fluxos e PKCE(Security Engineering)
Recomendamos completar os pré-requisitos antes de seguir, mas nada te impede de continuar.
Trade-offs
| Aspecto | Session (Redis) | JWT | Paseto v4 |
|---|---|---|---|
| Stateful | Sim | Não | Não |
| Revoke imediato | ✅ Trivial | ❌ Difícil (denylist ou TTL) | ❌ Difícil (mesmo problema) |
| Escala horizontal | OK com Redis | ✅ Perfeito | ✅ Perfeito |
| Payload bloat | N/A | ⚠️ Sim (claims viajam) | ⚠️ Sim |
| Algorithm confusion | N/A | ⚠️ Risco | ✅ Impossível |
| Edge/CDN-friendly | ❌ | ✅ | ✅ |
| Curva aprendizado | Baixa | Alta (pegadinhas) | Média |
AspectoStateful
Session (Redis)Sim
JWTNão
Paseto v4Não
AspectoRevoke imediato
Session (Redis)✅ Trivial
JWT❌ Difícil (denylist ou TTL)
Paseto v4❌ Difícil (mesmo problema)
AspectoEscala horizontal
Session (Redis)OK com Redis
JWT✅ Perfeito
Paseto v4✅ Perfeito
AspectoPayload bloat
Session (Redis)N/A
JWT⚠️ Sim (claims viajam)
Paseto v4⚠️ Sim
AspectoAlgorithm confusion
Session (Redis)N/A
JWT⚠️ Risco
Paseto v4✅ Impossível
AspectoEdge/CDN-friendly
Session (Redis)❌
JWT✅
Paseto v4✅
AspectoCurva aprendizado
Session (Redis)Baixa
JWTAlta (pegadinhas)
Paseto v4Média
Recomendação pragmática
- App B2C clássico (web monolito): session em Redis. Logout trivial, sem armadilha.
- API pública / mobile: access token curto (15min) + refresh token (7d) com rotation.
- Edge / serverless: JWT ou Paseto — stateless vence.
- Novo projeto greenfield: Paseto v4 > JWT pela DX e segurança por default.
Refresh token rotation
// Padrão: refresh uso único, rota nova a cada uso
async function refresh(oldRefreshToken: string) {
const stored = await db.refreshTokens.findUnique({ where: { text: oldRefreshToken } });
if (!stored || stored.usedAt) {
// REUSO DETECTADO — refresh roubado. Invalidar família inteira
if (stored) await db.refreshTokens.updateMany({
where: { familyId: stored.familyId },
data: { revokedAt: new Date() },
});
throw new Error('refresh reuse detected');
}
await db.refreshTokens.update({
where: { text: oldRefreshToken },
data: { usedAt: new Date() },
});
const newRefresh = crypto.randomBytes(32).toString('hex');
await db.refreshTokens.create({
data: { text: newRefresh, familyId: stored.familyId, userId: stored.userId },
});
return {
accessToken: signAccessToken(stored.userId, '15m'),
refreshToken: newRefresh,
};
}💡
Detectar reuse e invalidar FAMÍLIA é o padrão seguro. Auth0, Okta, Supabase fazem isso. Sem rotation, refresh roubado = acesso permanente.
Quiz rápido
Qual é a principal desvantagem de tokens autocontidos em relação a sessões no servidor?
Armadilhas do JWT
- alg: none — aceitar "sem assinatura". Lib moderna bloqueia; verifique.
- Algorithm confusion — sempre passe explicitamente.
- Claim bloat — token vira 4KB com 50 perms. Use reference token (opaco) ou busque perms do server.
- Clock skew — validar com tolerância (±30s).
- JTI + denylist — único modo de revoke real em JWT puro.
Perguntas frequentes
❓ Quando token autocontido é boa escolha?
Quando o serviço que valida não deveria consultar um estado central e a validade curta é aceitável — chamada entre serviços, borda. Ele deixa de ser boa escolha quando você precisa revogar imediatamente, porque revogar exige a lista central que o token existia para evitar.
❓ Sessão em servidor ainda é válida em 2026?
É, e para aplicação web costuma ser a escolha melhor: revogação imediata, tamanho pequeno no cliente e nenhum segredo verificável fora. O custo é o armazenamento compartilhado, que na prática é um cache que você provavelmente já tem.
❓ Onde guardar o token no navegador?
Em cookie com acesso restrito ao navegador, marcação de mesmo site e transporte seguro — nunca em armazenamento acessível por script, que fica exposto a qualquer injeção de código na página. Essa decisão é mais importante que a escolha do formato do token.
Fixando
Quiz rápido
Por que a rotação do token de renovação é recomendada?
Quiz rápido
Qual erro de implementação com tokens assinados é historicamente mais grave?
Terminou de ler?
Marcar como concluído registra o XP, mantém sua sequência e coloca 3 cartas deste módulo na fila de revisão espaçada.
Próximos passos sugeridos
Temas deste módulo
Discussão
Carregando comentários…