JWT ou sessão: qual usar pra autenticação?
Entenda a diferença entre autenticação baseada em sessão e em JWT, as vantagens e desvantagens reais de cada uma, o problema de revogar um token e onde guardar isso com segurança no navegador.
Já expliquei aqui como estruturar níveis de permissão com RBAC, mas antes de chegar na autorização tem uma pergunta mais básica que trava muita gente na hora de montar o login: uso sessão ou uso JWT? As duas resolvem autenticação, mas de um jeito bem diferente por baixo, e escolher sem entender o motivo costuma trazer dor de cabeça lá na frente.
Sessão: o jeito clássico
No modelo de sessão, quando o usuário faz login, o servidor cria um registro guardando quem é aquele usuário, geralmente num banco de dados ou no Redis. Esse registro recebe um identificador único, e esse identificador é o que vai para o navegador, dentro de um cookie.
Cookie: session_id=a1b2c3d4e5f6
A cada requisição, o navegador manda esse cookie de volta automaticamente. O servidor pega o session_id, busca o registro correspondente, e sabe quem está fazendo a requisição. O cookie em si não guarda informação nenhuma sobre o usuário, é só uma chave que aponta pra um registro que existe no servidor.
Isso tem uma consequência direta: como o estado mora no servidor, revogar uma sessão é trivial. Basta apagar aquele registro, e o session_id vira inútil na próxima requisição.
JWT: o jeito stateless
Com JWT (JSON Web Token), a lógica se inverte. Em vez do servidor guardar o estado, o próprio token carrega a informação, assinada digitalmente. Um JWT tem três partes: cabeçalho, payload e assinatura.
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjMiLCJyb2xlIjoiYWRtaW4ifQ.4f2a...
O payload no meio é só um JSON decodificável (não criptografado, decodificável por qualquer um), algo como { "sub": "123", "role": "admin" }. A parte que garante que ninguém alterou esse conteúdo é a assinatura, gerada com uma chave secreta que só o servidor conhece.
Quando uma requisição chega com esse token, o servidor não precisa consultar banco nenhum. Ele recalcula a assinatura usando a chave secreta e confere se bate com a que veio no token. Se bater, o conteúdo é confiável e o servidor já sabe quem é o usuário e quais claims ele tem, sem nenhuma consulta extra.
A vantagem do JWT é também o problema dele
Não precisar consultar banco a cada requisição é ótimo pra escala, principalmente em sistemas distribuídos, com vários serviços diferentes validando o mesmo token sem depender de um banco central. Mas essa mesma característica cria um problema real: como você revoga um JWT antes da expiração dele?
Na sessão, apagar o registro resolve na hora. No JWT, o token continua válido até expirar, porque a validação é só matemática, o servidor nem sabe que aquele token "deveria" ter sido invalidado. Se um token vazar, ele continua funcionando até o prazo dele acabar, mesmo que você troque a senha do usuário.
As soluções para isso existem, mas nenhuma é tão simples quanto apagar uma linha do banco:
- Expiração curta: token válido por poucos minutos, reduzindo a janela de risco.
- Blocklist: manter uma lista de tokens revogados, o que na prática reintroduz estado no servidor, meio que anulando a vantagem de ser stateless.
- Refresh token: um token de vida curta (o access token) usado nas requisições, e um token de vida longa (o refresh token), guardado com mais cuidado, usado só pra gerar um access token novo. Revogar o refresh token corta a renovação.
Na prática, a maioria dos sistemas sérios usa essa combinação de expiração curta mais refresh token, porque tenta pegar o melhor dos dois mundos.
Onde guardar o token
Isso aqui é onde a maior parte dos bugs de segurança em JWT realmente mora, e não tem nada a ver com o token em si. Guardar o JWT em localStorage é comum, mas expõe ele a qualquer script rodando na página, inclusive um script malicioso injetado por uma falha de XSS.
O caminho mais seguro é o mesmo da sessão: um cookie httpOnly, que o JavaScript do navegador não consegue ler, combinado com Secure (só trafega em HTTPS) e SameSite (limita em quais contextos o cookie é enviado). Isso reduz bastante a superfície de ataque, mesmo usando JWT.
Qual usar na prática
Se o seu sistema é um monólito com um único backend e você não precisa escalar validação de token entre vários serviços, sessão costuma ser mais simples e mais segura por padrão, porque revogar é imediato e você não precisa se preocupar com blocklist.
Se você tem múltiplos serviços, uma API pública consumida por terceiros, ou precisa de autenticação sem depender de um banco central a cada chamada, JWT com expiração curta e refresh token costuma valer o esforço extra.
Não existe certo universal aqui, existe o que resolve o problema que você tem. O erro mais comum não é escolher JWT ou sessão, é escolher um dos dois sem entender o trade-off de revogação que vem junto.
Leia também