Como ler um JWT — e por que decodificar não é validar
O que significam as três partes de um JSON Web Token, como ler claims como exp e iat, e por que um token decodificado não prova nada sem validação.
Por Felipe YamaokaPublicado em 5 de novembro de 20266 min de leitura
O token que aparece no log
Mais cedo ou mais tarde todo dev esbarra numa string como esta, num header Authorization, num cookie ou numa linha de log:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI0MiIsIm5hbWUiOiJBZGEgTG92ZWxhY2UiLCJyb2xlIjoiYWRtaW4iLCJpYXQiOjE3NjcyMjU2MDAsImV4cCI6MTc2NzIyOTIwMH0.fzONOB8GmSymQ8FV5MeQu77nX-m0-7MQ2-eJfTK_EmEParece criptografado, mas não é. Isso é um JSON Web Token (JWT), e quase tudo nele é JSON comum dentro de uma codificação que qualquer pessoa consegue desfazer. Saber ler um JWT poupa muito tempo quando o login falha, quando a API responde 401 ou quando o usuário diz que foi deslogado do nada. Saber o que essa leitura não prova evita que você escreva uma falha de segurança.
Este guia mostra as três partes do token, as claims que mais aparecem e a diferença entre decodificar e validar.
Três partes separadas por pontos
No formato compacto, o mais comum, um JWT tem exatamente três trechos separados por .:
- Header: um objeto JSON que descreve o token, principalmente o algoritmo de assinatura.
- Payload: um objeto JSON com as claims, as afirmações que o token faz sobre um usuário ou uma sessão.
- Assinatura: bytes que provam que as duas primeiras partes foram geradas por quem tem a chave.
Cada trecho é codificado em Base64URL, uma variante do Base64 que troca + e / por - e _ e tira o preenchimento =, para o token circular em URLs e headers sem precisar de escape. Base64URL é codificação, não criptografia: não existe chave nenhuma para transformar o texto de volta.
Decodificando o primeiro trecho do token acima, sai o header:
{
"alg": "HS256",
"typ": "JWT"
}E o segundo trecho vira o payload:
{
"sub": "42",
"name": "Ada Lovelace",
"role": "admin",
"iat": 1767225600,
"exp": 1767229200
}O terceiro trecho não é JSON. É a assinatura em si, também em Base64URL, e ela só faz sentido para um programa que tenha a chave.
As claims que você mais vai ver
A especificação do JWT, a RFC 7519, registra alguns nomes curtos de claims. O seu provedor de identidade pode acrescentar vários outros, mas vale reconhecer estes de cara:
sub(subject): de quem o token fala, normalmente o id do usuário. Trate como texto, mesmo quando parece número.iss(issuer): quem emitiu o token, por exemplo a URL do servidor de autenticação.aud(audience): para quem o token foi emitido. Uma API deve recusar um token feito para outra audiência.exp(expiration time): depois desse momento o token tem que ser recusado.nbf(not before): antes desse momento o token tem que ser recusado.iat(issued at): quando o token foi criado.jti(JWT id): um id único, útil para bloquear um token específico.
As claims de tempo são as que mais confundem, porque são números como 1767229200. Elas estão em segundos desde 1º de janeiro de 1970 em UTC, a chamada época Unix, e não em milissegundos. No exemplo, iat é 1º de janeiro de 2026 às 00:00 UTC, ou 31 de dezembro de 2025 às 21:00 no horário de Brasília, e exp é uma hora depois. Se você passar exp direto para new Date() no JavaScript, que espera milissegundos, vai ganhar uma data em janeiro de 1970 e um token que parece vencido desde sempre. Multiplique por 1000 antes. O Conversor de Timestamp Unix mostra as duas leituras lado a lado quando bater a dúvida.
Lendo um token com segurança
Quando uma requisição falha com 401, decodificar o token costuma mostrar o motivo em segundos:
- Expirado: compare
expcom o horário atual. Um token vencido há poucos segundos geralmente indica um cliente que não renova o token a tempo. - Relógio desalinhado: se
iatounbfestiver no futuro, o relógio do servidor que emitiu o token e o do servidor que confere não batem. Por isso a maioria das bibliotecas aceita uma pequena tolerância, de 30 ou 60 segundos. - Audiência ou emissor errado: um token do ambiente de homologação enviado para produção falha aqui.
- Claim faltando: a API espera um
roleou umscopeque esse tipo de token não traz.
Um decodificador que roda no navegador é o lugar certo para fazer isso. Colar um token de verdade num site que envia o texto para um servidor é má ideia: enquanto vale, um token funciona como senha. O Decodificador JWT deste site faz tudo localmente, aceita o token com ou sem o prefixo Bearer e mostra as claims de tempo no seu horário local, sem que nada saia do seu dispositivo.
Mesmo assim, evite colar tokens de produção em chamados, chats ou prints. Se um vazar, revogue a sessão.
Decodificar não é validar
Esta é a parte mais importante. Decodificar um token mostra o que ele diz. Não mostra se isso é verdade.
Qualquer pessoa consegue montar um token. Pegue o payload acima, troque "role": "admin" pelo que quiser, codifique de novo e cole no header e na assinatura originais: o resultado continua decodificando perfeitamente. O que dá valor ao token é a assinatura, e conferir a assinatura exige a chave:
- No
HS256e nos outros algoritmos HMAC, o mesmo segredo assina e valida. Só o emissor e as APIs que confiam nele devem conhecê-lo. - No
RS256, noES256e nos outros algoritmos assimétricos, uma chave privada assina e uma chave pública valida. Os provedores de identidade costumam publicar as chaves públicas num endpoint JWKS.
Validar significa recalcular a assinatura sobre header.payload com a chave, comparar com o terceiro trecho e então conferir as claims: exp, nbf, iss e aud. Na prática ninguém escreve isso na mão; você chama uma biblioteca mantida e informa os valores esperados. Um exemplo mínimo em Node.js com o pacote jose:
import { jwtVerify } from "jose";
const secret = new TextEncoder().encode(process.env.JWT_SECRET);
const { payload } = await jwtVerify(token, secret, {
issuer: "https://auth.example.com",
audience: "orders-api",
algorithms: ["HS256"],
});Repare na opção algorithms. Um ataque clássico troca o header para "alg": "none" ou substitui um algoritmo assimétrico por um simétrico, apostando que o servidor confia no que o header diz. Sempre informe à biblioteca quais algoritmos você aceita.
Erros comuns
- Confiar no payload no front-end. Ler o
roleno navegador para decidir o que mostrar na tela tudo bem. Decidir o que o usuário pode fazer tem que acontecer no servidor, depois da validação. - Usar o token para guardar segredos. Qualquer pessoa que veja o token consegue ler o payload. Nunca coloque senhas, CPF, documentos ou anotações internas ali.
- Confundir segundos e milissegundos.
expestá em segundos.Date.now()está em milissegundos. - Ignorar o relógio. Um servidor com o horário errado recusa tokens válidos ou aceita tokens vencidos.
- Expiração longa demais. Um token roubado vale até o
exp. Access tokens de vida curta, com refresh token, limitam o estrago. - Conferir só a assinatura. Uma assinatura válida num token feito para outra audiência continua sendo o token errado.
Resumo
Um JWT tem três trechos em Base64URL: um header, um payload com as claims e uma assinatura. Decodificar os dois primeiros é seguro, instantâneo e o jeito mais rápido de depurar autenticação, principalmente pelas claims exp, iat, iss e aud. Mas um token decodificado é só uma afirmação. Antes de confiar em qualquer coisa dele, valide a assinatura com a chave certa, os algoritmos aceitos e o emissor e a audiência esperados.
Leia também
Base64 não é criptografia — o que é, quando usar e a variante URL-safe
Como o Base64 transforma bytes em texto, por que ele não esconde nada, como tratar acentos e emoji do jeito certo e quando usar o alfabeto URL-safe.
5 de novembro de 20265 min de leituraFerramenta: Codificador/Decodificador Base64
A regex que trava o navegador — backtracking catastrófico explicado
Por que uma expressão regular curta pode levar segundos ou travar a página, como reconhecer quantificadores aninhados e como reescrever o padrão.
5 de novembro de 20266 min de leituraFerramenta: Testador de Regex