Para decodificar um token JWT, divida a string nos dois pontos em três partes e, em seguida, decodifique o cabeçalho e o payload com base64url. O payload é um JSON simples que qualquer um pode ler, porque um JWT é codificado, não criptografado. Em JavaScript, você pode fazer isso em uma linha com atob, mas apenas após corrigir os caracteres base64url que quebram uma decodificação ingênua. Este guia mostra exatamente como decodificar um token JWT manualmente, o detalhe que a maioria dos trechos de código ignora e por que decodificar não diz nada sobre se o token é real.
Se você já colou um token em algum lugar e viu as reivindicações aparecerem instantaneamente, essa é a parte que confunde as pessoas. Não há chave secreta envolvida na leitura de um JWT. A privacidade vem da assinatura, não de esconder o conteúdo, e confundir essas duas ideias é como desenvolvedores acabam enviando bugs reais. Vamos decodificar um corretamente primeiro e depois esclarecer o mal-entendido perigoso por trás disso.
Como um JWT Realmente se Parece#
Um JSON Web Token é uma única string composta por três partes separadas por pontos:
header.payload.signature
Um token real se parece com isto (aqui quebrado em linhas para legibilidade, mas é uma string contínua):
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkphbmUgRG9lIiwiaWF0IjoxNzE3NzI4MDAwLCJleHAiOjE3MTc3MzE2MDB9.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
Cada parte tem uma função:
- Header: um pequeno objeto JSON que nomeia o algoritmo de assinatura (
alg) e o tipo do token (typ). Codificado em Base64url. - Payload: o objeto JSON que contém suas declarações (id do usuário, papéis, expiração, qualquer coisa que você colocar lá). Codificado em Base64url.
- Signature: um hash criptográfico do header e payload, calculado com uma chave secreta ou privada. Esta é a única parte que prova que o token não foi adulterado.
O header e o payload não estão ocultos. Eles estão em base64url, que é reversível por qualquer pessoa sem chave. Trate tudo em um payload JWT como informação pública.
Por que base64url, e não base64 comum#
JWTs são usados em URLs, cabeçalhos HTTP e cookies. O base64 padrão usa os caracteres +, / e o preenchimento = no final, todos inseguros ou problemáticos nesses contextos. Por isso, JWTs usam base64url, definido na RFC 4648, que substitui + por -, / por _ e remove o preenchimento = completamente.
Essa única diferença é o motivo pelo qual a maioria dos snippets de decodificação copiados e colados falham em tokens reais. Se você decodificar em base64 um payload que contém - ou _ sem convertê-lo primeiro, obterá saída distorcida ou um erro. Nós lidamos com isso explicitamente abaixo. Se quiser ver os mecanismos de codificação por conta própria, nosso codificador e decodificador base64 permite que você experimente com saída padrão versus URL-safe.
Como Decodificar um Token JWT em JavaScript#
O navegador oferece atob para decodificação base64. O truque é converter base64url de volta para base64 padrão primeiro, depois lidar corretamente com Unicode. Aqui está a versão completa e correta, não a linha única quebrada que você costuma encontrar.
Passo 1: Divida o token nos pontos#
O token é header.payload.signature. Divida e pegue as partes que deseja.
const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkphbmUgRG9lIn0.signature_here";
const [headerB64, payloadB64, signatureB64] = token.split(".");
Se token.split(".") não retornar exatamente três partes, a string não é um JWT bem formado e você deve parar aqui em vez de decodificar lixo.
Passo 2: Converta base64url de volta para base64 padrão#
Esta é a etapa que quase todo snippet rápido ignora, e é exatamente por isso que esses snippets quebram em tokens de produção que contêm - ou _.
function base64UrlToBase64(input) {
// Substitui caracteres seguros para URL por caracteres base64 padrão
let output = input.replace(/-/g, "+").replace(/_/g, "/");
// Re-adiciona o padding que o base64url removeu
const pad = output.length % 4;
if (pad) {
output += "=".repeat(4 - pad);
}
return output;
}
Sem restaurar o padding, alguns ambientes lançam um InvalidCharacterError no atob. Com ele, você decodifica de forma confiável toda vez.
Passo 3: Decodifique com atob e analise o JSON#
Agora atob funciona, mas há mais um problema. atob retorna uma string binária, então qualquer caractere não ASCII (nomes acentuados, emoji, scripts não latinos) sai distorcido. Decodifique os bytes como UTF-8 para ter segurança.
function decodeJwtPart(part) {
const base64 = base64UrlToBase64(part);
const binary = atob(base64);
// Converte a string binária para uma string UTF-8 adequada
const bytes = Uint8Array.from(binary, (c) => c.charCodeAt(0));
const json = new TextDecoder("utf-8").decode(bytes);
return JSON.parse(json);
}
const header = decodeJwtPart(headerB64);
const payload = decodeJwtPart(payloadB64);
console.log(header); // { alg: "HS256", typ: "JWT" }
console.log(payload); // { sub: "1234567890", name: "Jane Doe", ... }
Passo 4: Leia as claims que você precisa#
O payload agora é um objeto JavaScript normal. Extraia as claims padrão:
console.log("Assunto:", payload.sub);
console.log("Emitido em:", new Date(payload.iat * 1000).toISOString());
console.log("Expira em:", new Date(payload.exp * 1000).toISOString());
console.log("Expirado?", payload.exp * 1000 < Date.now());
Note que iat e exp são timestamps Unix em segundos, então multiplique por 1000 antes de passá-los para Date. Esquecer isso dá timestamps em 1970 e uma tarde muito confusa.
Uma nota sobre Node.js#
No Node, atob existe em versões modernas, mas a decodificação idiomática usa Buffer, que lida com base64url e UTF-8 de uma só vez:
const payload = JSON.parse(
Buffer.from(payloadB64, "base64url").toString("utf8")
);
A flag de codificação "base64url" faz a troca de caracteres e o padding para você, por isso o código Node parece mais limpo que o código do navegador para esta tarefa.
Decodificar não é verificar: o erro que causa violações#
Aqui está a parte que os trechos nunca avisam, e é a coisa mais importante desta página. Decodificar um JWT apenas lê as declarações. Não confirma que essas declarações são verdadeiras.
Qualquer um pode pegar um token, alterar o payload (por exemplo, trocar "role": "user" por "role": "admin"), recodificá-lo e devolvê-lo ao seu servidor. O payload decodificado parecerá perfeitamente válido. A única coisa entre esse token falsificado e seus dados é a verificação de assinatura, que requer a chave secreta ou pública e que a decodificação nunca toca.
| Ação | Precisa de uma chave? | O que prova |
|---|---|---|
| Decodificar cabeçalho/payload | Não | O que o token alega dizer |
| Verificar assinatura | Sim | Que o token foi emitido por você e não foi modificado |
Verificar exp / nbf | Não (mas somente após verificar) | Que o token está dentro de sua janela de tempo válida |
A regra é simples e absoluta:
Nunca confie em um payload de JWT decodificado para tomar uma decisão de segurança. Decodifique-o apenas para exibição e depuração. No servidor, sempre verifique a assinatura com uma biblioteca confiável antes de agir com base em qualquer declaração.
Um token decodificado é uma alegação, não um fato. Tratar o payload como verdade absoluta porque "parece certo" é exatamente como bugs de escalonamento de privilégios são enviados. Para a análise completa de por que essas são duas operações diferentes e onde as equipes se queimam, leia nosso guia sobre decodificar versus verificar na segurança JWT.
"Um JWT é criptografado?" Não, e isso importa#
Este é o equívoco por trás da maioria dos erros com JWT. Um JWT assinado padrão (um JWS) é codificado, não criptografado. O payload é totalmente legível por qualquer um que intercepte o token. A assinatura protege a integridade (ninguém pode alterá-lo sem ser detectado), não a confidencialidade (qualquer um pode lê-lo).
As consequências práticas:
- Não coloque segredos em um payload de JWT. Sem senhas, sem chaves de API, sem dados pessoais que você não imprimiria em um outdoor. Está a uma decodificação base64url de ser lido.
- Se você realmente precisa de confidencialidade, use JWE (JSON Web Encryption), um formato diferente e menos comum que realmente criptografa o payload.
- Presuma que todo JWT que você emitir será inspecionado. Logs, ferramentas de desenvolvedor do navegador, proxies e qualquer decodificador podem lê-lo.
Lendo as Claims Padrão#
O payload pode conter qualquer coisa, mas um conjunto de claims registradas tem significados definidos. Conhecê-las ajuda a depurar problemas de autenticação rapidamente.
| Claim | Nome | Significado |
|---|---|---|
iss | Emissor | Quem criou e assinou o token |
sub | Sujeito | A quem o token se refere, geralmente um ID de usuário |
aud | Audiência | Para quem o token é destinado |
exp | Expiração | Tempo Unix (segundos) após o qual o token é inválido |
nbf | Não antes | Tempo Unix antes do qual o token é inválido |
iat | Emitido em | Tempo Unix em que o token foi criado |
jti | ID do JWT | Um identificador único para o token |
Quando você estiver investigando um bug do tipo "por que este usuário está sendo desconectado", decodifique o token e verifique exp primeiro. Um exp expirado ou um problema de diferença de relógio com nbf é o culpado mais comum, e você pode identificá-lo em segundos assim que o payload estiver legível.
Quando usar uma ferramenta de decodificação#
Escrever a função de decodificação vale a pena uma vez para entender o funcionamento. Mas, para depuração do dia a dia, colar uma função no console para cada token é lento, e um copiar-e-colar errado reintroduz o bug do base64url que você acabou de corrigir.
Um decodificador baseado em navegador é mais rápido e seguro para inspeção:
- Ele divide, converte e exibe de forma organizada o cabeçalho e o payload instantaneamente.
- Ele sinaliza um
expexpirado, evitando que você precise fazer o cálculo do timestamp. - Ele roda no seu navegador, então o token não é enviado para um servidor (importante, já que o token é uma credencial ativa).
Esse último ponto é mais relevante do que as pessoas imaginam. Um JWT geralmente é um token de sessão ativo. Colá-lo em um decodificador online duvidoso que o envia para um backend é entregar credenciais funcionais. Nosso decodificador JWT gratuito decodifica inteiramente no seu navegador, mostra o cabeçalho, o payload e uma data de expiração legível, e nunca transmite o token para lugar nenhum. Para o contexto de segurança sobre o que um decodificador pode ou não revelar, nosso guia de segurança de decodificador JWT e tokens aprofunda hábitos seguros de inspeção.
Como Decodificar um Token JWT com Segurança: O Resumo#
Para decodificar um token JWT, divida-o pelos pontos, converta cada parte base64url para base64 padrão (substitua - e _, adicione padding), execute atob e analise o JSON. No Node, Buffer.from(part, "base64url") faz a conversão para você. O cabeçalho informa o algoritmo, a carga útil contém suas reivindicações, e iat e exp são segundos desde 1970.
A parte inegociável para levar: decodificar é ler, não confiar. Uma carga útil JWT é pública, reversível e forjável. Decodifique-a livremente para exibição e depuração, mas tome toda decisão real de autorização no servidor após verificar a assinatura com uma chave. Entenda essa distinção e JWTs são simples. Confundi-la e você terá uma falha de segurança que parece código funcional.
Perguntas Frequentes#
Como decodificar um token JWT sem uma biblioteca?
Divida o token pelos pontos em header, payload e signature, depois decodifique o header e o payload com base64url. No navegador, converta base64url para base64 (substitua - por +, _ por / e readicione o preenchimento =) antes de chamar atob, então use JSON.parse no resultado. No Node, Buffer.from(part, "base64url").toString("utf8") faz isso em uma linha.
Por que minha chamada atob falha em um JWT?
Porque JWTs usam base64url, não base64 padrão. Eles contêm - e _ em vez de + e /, e removem o preenchimento =. Passar essa string bruta para atob gera um erro ou retorna lixo. Converta os caracteres e restaure o preenchimento primeiro, que é a etapa que a maioria dos trechos rápidos omite.
Um JWT é criptografado ou apenas codificado? Um JWT assinado padrão é codificado, não criptografado. O payload está em base64url, que qualquer um pode reverter sem chave, então trate seu conteúdo como público. A assinatura protege contra adulteração, não contra leitura. Se você precisar que o payload seja confidencial, use JWE (JSON Web Encryption).
Alguém pode ler ou alterar meu payload JWT? Qualquer um que tenha o token pode ler o payload instantaneamente, já que é apenas base64url. Eles também podem alterá-lo e recodificá-lo. O que não podem fazer é produzir uma assinatura válida sem sua chave secreta ou privada, por isso seu servidor deve verificar a assinatura em vez de confiar nas claims decodificadas.
Decodificar um JWT verifica se ele é válido?
Não. Decodificar apenas lê o que o token alega. A verificação é uma etapa separada que confere a assinatura com sua chave para confirmar que o token é autêntico e não modificado, e depois verifica exp e nbf para temporização. Nunca tome uma decisão de autorização baseada apenas no payload decodificado.
O que significam iat e exp em um JWT?
São timestamps. iat (issued at) é quando o token foi criado, e exp (expiration) é quando ele deixa de ser válido. Ambos são tempo Unix em segundos, então multiplique por 1000 antes de construir um Date do JavaScript. Um token está expirado quando exp * 1000 é menor que o horário atual.



