Skip to content
Voltar ao blog
Segurança

Como decodificar um token JWT: guia completo para desenvolvedores

Decodifique JWT com segurança: anatomia, base64url e exemplos em Node.js, Python e Go. Inspecione header, payload e claims com um decodificador JWT online e privado.

12 min de leitura

Como decodificar um token JWT: guia completo para desenvolvedores

Sua API acabou de retornar 401 Unauthorized. O header Authorization: Bearer eyJhbGciOi... parece certo. O token expirou? A audience está errada? O emissor rotacionou uma chave? Você não consegue responder a nada disso sem ler o que de fato está dentro do token, e para decodificar um JWT você não precisa de segredo, biblioteca, nem mesmo de conexão de rede. Um JWT são três blocos codificados em base64url unidos por pontos. Decodificar é mecânico: split, base64url, JSON.parse. Sem mágica, sem criptografia.

Este guia percorre a anatomia, mostra como decodificar um JWT em Node.js, Python, Go e no navegador, explica a diferença entre decodificar e verificar (que confunde a maioria das equipes) e lista os modos de falha reais que vão te pegar. Se você só precisa inspecionar um token agora, pule para o nosso decodificador JWT gratuito. Ele roda inteiramente no seu navegador, então tokens de produção nunca saem do seu dispositivo.

O que é um JWT? (anatomia rápida)

Um JSON Web Token (JWT) é uma credencial compacta e segura para URLs, definida na RFC 7519. Ele carrega claims (dados sobre o usuário e sobre o próprio token) entre duas partes. Um JWT são três pedaços codificados em base64url unidos por pontos: um header, um payload e uma assinatura.

Aqui está um token real separado para você ver a estrutura:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9       ← header
.
eyJzdWIiOiJ1c2VyXzEyMyIsImV4cCI6MTk5OTk5OTk5OX0   ← payload
.
4NhxPjwoZxPNuxG-2C5ugGxaUsUJ0QyskAz7Ymz5Sg0       ← signature

O header descreve como o token foi assinado, normalmente { "alg": "HS256", "typ": "JWT" }. O payload carrega as claims, tanto as registradas (sub, exp, iat) quanto as customizadas (role, tenant). A assinatura é uma prova criptográfica calculada sobre o header e o payload que permite ao destinatário detectar adulteração. Base64url é uma variante de base64 segura para URLs; consulte nosso guia de Base64 para iniciantes para uma introdução de dez minutos.

Você vai encontrar JWTs em todo lugar onde vive a autenticação moderna: access tokens do OAuth 2.0, ID tokens do OpenID Connect, credenciais de API emitidas por Auth0, Okta, Clerk, Supabase, Firebase, e tokens trocados entre microserviços dentro de uma mesh. Eles são o formato de credencial padrão da última década.

Antes de avançar, uma frase que você precisa internalizar: JWTs são codificados, não criptografados. Qualquer pessoa com o token pode ler todas as claims. A assinatura prova origem; não esconde o conteúdo. Esse fato único dita todo o resto deste guia: o que é seguro colocar no payload, por que decodificar não precisa de segredo, por que a verificação da assinatura é inegociável no lado do servidor.

Como funciona a decodificação de JWT (base64url, não descriptografia)

Decodificar um JWT não é uma operação criptográfica. São quatro passos mecânicos:

  1. Divida o token em . em exatamente três segmentos.
  2. Decodifique o primeiro segmento com base64url e faça parse como JSON. Esse é o header.
  3. Decodifique o segundo segmento com base64url e faça parse como JSON. Esse é o payload.
  4. Deixe o terceiro segmento (a assinatura) como bytes brutos. Verificá-lo exige a chave.

Esse é o algoritmo inteiro. Nenhuma biblioteca é obrigatória. Qualquer linguagem com base64 e um parser JSON decodifica um JWT em cinco linhas. Nosso codificador/decodificador Base64 faz os passos 2 e 3 manualmente se você quiser ver a mecânica.

O que é base64url?

Base64url é base64 comum com três ajustes para que a saída seja segura em URLs e headers HTTP: - substitui +, _ substitui /, e o padding = no final é descartado. Se você jogar base64url puro em um decodificador base64 padrão sem reverter essas substituições, vai receber lixo ou um erro. O guia avançado de Base64 cobre os casos extremos de padding em profundidade.

Base64 padrãobase64url
AlfabetoA-Z a-z 0-9 + /A-Z a-z 0-9 - _
Padding= obrigatório no finalRemovido
Seguro para URL?NãoSim
ExemploPDw/Pz8+PDw_Pz8-

Mais uma observação que vale a tinta: você não pode descriptografar a assinatura no cliente. Decodificar é um caminho de mão única, dos bytes codificados para o JSON. Verificar a assinatura é uma operação separada que precisa do segredo HMAC (para algoritmos da família HS) ou da chave pública do emissor (para RS, PS, ES, EdDSA).

Por que você não precisa do segredo para decodificar

Porque o payload é base64url mais JSON, não texto cifrado. Um segredo só entra em cena quando você quer provar que o token não foi adulterado, ou seja, numa verificação de assinatura. Qualquer pessoa no caminho de rede, qualquer pessoa com o token numa linha de log, qualquer pessoa com um navegador pode ler todas as claims que você colocou nele. Por isso você nunca deve colocar senhas, chaves de API ou PII além do que o destinatário já sabe dentro de um payload JWT. Para o modelo de ameaça mais amplo, leia nosso guia de boas práticas de segurança.

Decodifique JWT online em 3 cliques — decodificador JWT gratuito

Às vezes você só precisa de uma resposta agora. Esse token expirou? A claim aud é o que eu acho que é? O header diz alg:none? O caminho mais rápido é nosso decodificador JWT online. Ele foi feito para aquele plantão das 2 da manhã.

  1. Cole o token completo na área de entrada. Inclua os três segmentos separados por ponto.
  2. Leia o header e o payload decodificados, além dos chips de status no topo: algoritmo, issued-at, expiração e um badge vermelho Expirado se exp já estiver no passado.
  3. Copie o painel que você precisar para o bug report, thread do Slack ou fixture de teste.

Por que é seguro para tokens reais de produção:

  • 100% baseado no navegador. A decodificação usa atob nativo e JSON.parse. Nenhuma requisição de rede, nunca.
  • Sem logs, sem tracking, sem cookies, sem cadastro.
  • Funciona offline depois que a página carregou.

A ferramenta JWT Decoder é agnóstica quanto ao algoritmo. Como decodificar só precisa de base64url e JSON, ela lê todas as variantes de JWS: HS256/384/512, RS256/384/512, PS256/384/512, ES256/384/512, EdDSA e alg:none. Só a verificação da assinatura depende do algoritmo, e verificação não é algo que você queira que uma ferramenta web pública faça. Mais sobre isso daqui a pouco.

Precisa decodificar um segmento em base64 manualmente para conferir a ferramenta? Use nosso codificador/decodificador Base64 e coloque cada segmento como base64url.

Como decodificar JWT em código (Node.js, Python, Go, navegador)

Para tudo que não é debug interativo (middleware, testes, scripts de migração, ferramentas de CLI), você vai recorrer a uma biblioteca. Aqui está o código mínimo para decodificar um JWT nos quatro ambientes em que você tem maior probabilidade de esbarrar, com o caminho somente-leitura e o de verificação lado a lado. Todo snippet é copiável e produz as saídas mostradas nos comentários.

Decodificar JWT em Node.js (jsonwebtoken)

// npm install jsonwebtoken
const jwt = require('jsonwebtoken');

const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9' +
              '.eyJzdWIiOiJ1c2VyXzEyMyIsImV4cCI6MTk5OTk5OTk5OX0' +
              '.4NhxPjwoZxPNuxG-2C5ugGxaUsUJ0QyskAz7Ymz5Sg0';

// Apenas decodifica — NÃO verifica a assinatura
const decoded = jwt.decode(token, { complete: true });
console.log(decoded.header);   // { alg: 'HS256', typ: 'JWT' }
console.log(decoded.payload);  // { sub: 'user_123', exp: 1999999999 }

// Verificar — o caminho de produção
const secret = process.env.JWT_SECRET;
const verified = jwt.verify(token, secret, { algorithms: ['HS256'] });

Sempre passe uma allowlist explícita de algorithms para verify. Historicamente, omitir isso permitiu que atacantes rebaixassem um token RS256 para HS256 assinando com a chave pública como segredo HMAC. É o clássico ataque de confusão de algoritmo. A allowlist é sua defesa.

Decodificar JWT em Python (PyJWT)

# pip install PyJWT
import jwt

token = (
    "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"
    ".eyJzdWIiOiJ1c2VyXzEyMyIsImV4cCI6MTk5OTk5OTk5OX0"
    ".4NhxPjwoZxPNuxG-2C5ugGxaUsUJ0QyskAz7Ymz5Sg0"
)

# Apenas decodifica — inseguro para autenticação, ok para inspeção
decoded = jwt.decode(token, options={"verify_signature": False})
print(decoded)  # {'sub': 'user_123', 'exp': 1999999999}

# Header sem tocar no payload
header = jwt.get_unverified_header(token)
print(header)   # {'alg': 'HS256', 'typ': 'JWT'}

# Verificar — caminho de produção
payload = jwt.decode(
    token,
    key="your-hs256-secret",
    algorithms=["HS256"],
    audience="api.example.com",
)

O PyJWT se recusa a verificar sem uma lista algorithms, um padrão sensato que evita o mesmo ataque de confusão sobre o qual o exemplo de Node alerta.

Decodificar JWT em Go (golang-jwt/jwt/v5)

// go get github.com/golang-jwt/jwt/v5
package main

import (
    "fmt"
    "github.com/golang-jwt/jwt/v5"
)

func main() {
    tokenString := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" +
        ".eyJzdWIiOiJ1c2VyXzEyMyIsImV4cCI6MTk5OTk5OTk5OX0" +
        ".4NhxPjwoZxPNuxG-2C5ugGxaUsUJ0QyskAz7Ymz5Sg0"

    // Apenas decodifica
    parser := jwt.NewParser()
    claims := jwt.MapClaims{}
    _, _, err := parser.ParseUnverified(tokenString, claims)
    if err != nil {
        panic(err)
    }
    fmt.Println(claims["sub"], claims["exp"]) // user_123 1.999999999e+09

    // Verificar
    secret := []byte("your-hs256-secret")
    token, err := jwt.Parse(tokenString, func(t *jwt.Token) (interface{}, error) {
        if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
            return nil, fmt.Errorf("unexpected alg: %v", t.Header["alg"])
        }
        return secret, nil
    })
    fmt.Println(token.Valid, err)
}

A closure keyFunc é onde você impõe a família do algoritmo: rejeite qualquer coisa que não seja o método esperado antes de devolver a chave.

Decodificar JWT no navegador (zero dependências)

Às vezes você não quer dependência nenhuma: um painel de debug rápido, uma extensão de navegador, um pequeno badge de UI que mostra o papel do usuário atual. As APIs nativas do navegador bastam:

function decodeJwt(token) {
  const [h, p] = token.split('.');
  const pad = (s) => s + '==='.slice((s.length + 3) % 4);
  const decodeSegment = (s) => {
    const b64 = pad(s).replace(/-/g, '+').replace(/_/g, '/');
    const bytes = Uint8Array.from(atob(b64), (c) => c.charCodeAt(0));
    return JSON.parse(new TextDecoder().decode(bytes));
  };
  return { header: decodeSegment(h), payload: decodeSegment(p) };
}

const { header, payload } = decodeJwt(token);
console.log(header);   // { alg: 'HS256', typ: 'JWT' }
console.log(payload);  // { sub: 'user_123', exp: 1999999999 }

A passagem por TextDecoder importa para qualquer token com payload não-ASCII (emoji em um nome de exibição, cirílico em um preferred_username). O atob puro retorna uma binary string, o que quebra o JSON.parse em UTF-8 multi-byte. É exatamente isso que nosso decodificador JWT online roda localmente no seu navegador, menos a UI.

Tabela comparativa

LinguagemApenas decodificarVerificarBiblioteca
Node.jsjwt.decode(token)jwt.verify(token, key, { algorithms: [...] })jsonwebtoken
Pythonjwt.decode(token, options={"verify_signature": False})jwt.decode(token, key, algorithms=[...])PyJWT
Goparser.ParseUnverified(token, claims)jwt.Parse(token, keyFunc)golang-jwt/jwt/v5
Navegadoratob + TextDecoder + JSON.parsePergunte ao seu backend

Decodificar vs verificar — a diferença crítica

Decodificar um JWT lê as claims; verificar um JWT prova que essas claims não foram adulteradas. Decodificar nunca confia; verificar é a confiança. Essa é a única distinção que separa uma implementação de auth que funciona de uma CVE.

DecodificarVerificar
Precisa de segredo/chave?NãoSim
Roda no cliente?Com segurançaNunca
Prova autenticidade?NãoSim
Confere expiração?OpcionalSim
Caso de usoDebug, inspeçãoAutenticação, autorização

Nunca tome uma decisão de autorização a partir de um JWT decodificado (não verificado). Nem em middleware, nem em um hook React, nem em uma função serverless que você acha que está atrás do gateway. Claims decodificadas dizem o que o token alega; claims verificadas dizem o que o emissor assinou. Um atacante que entrega ao seu servidor um token forjado à mão sem assinatura válida pode colocar o que quiser no payload, e só a verificação da assinatura rejeita isso.

Mais um detalhe crítico sobre a família HMAC: se você usa HS256, a entropia do seu segredo é o jogo inteiro. Um segredo curto e adivinhável sofre brute-force offline contra qualquer token que o atacante capturar, e aí ele emite os próprios tokens e entra pela porta da frente. Use pelo menos 256 bits de aleatoriedade real. Veja nosso guia sobre a força de segredos HMAC para a matemática de por que isso importa.

Referência de claims comuns do JWT

Todo JWT que você encontrar usa algum subconjunto das claims registradas na RFC 7519. Memorize a lista curta:

ClaimNomeExemploNotas
issIssuerhttps://auth.example.comQuem emitiu o token
subSubjectuser_123Normalmente o ID do usuário
audAudienceapi.example.comPara quem o token é — deve bater no servidor
expExpiration1715003600Segundos Unix; no passado = expirado
iatIssued At1715000000Segundos Unix em que o token foi emitido
nbfNot Before1715000060Primeiro instante em que o token é utilizável
jtiJWT IDd1f8…Único por token; previne replay
kidKey ID (header)key-2025-01Qual chave do seu JWKS assinou este token

Claims específicas da aplicação ficam ao lado dessas: role, scope, email, tenant_id, o que seu provedor de identidade emitir. Mantenha-as curtas, porque cada byte viaja junto em toda requisição.

Para ler datas humanas a partir de iat e exp, experimente nosso conversor de timestamp Unix. Cole o número, receba a data no seu fuso local, detecte um bug de skew em um segundo.

Troubleshooting — por que meu JWT não decodifica?

Cinco modos de falha reais, em ordem aproximada de frequência. Cada um vem no formato Sintoma → Causa → Solução.

  1. “Formato JWT inválido — esperava três segmentos.” Você copiou só o payload, ou o shell quebrou o token em várias linhas e você pegou apenas a primeira. Solução: copie de novo o valor xxx.yyy.zzz completo a partir do corpo da resposta original, não de um terminal renderizado. Valores longos em linha única sobrevivem melhor numa aba Network de devtools do navegador do que num terminal com scroll.
  2. Cinco segmentos em vez de três. Você tem um JWE (JWT criptografado), não um JWS. O formato é header.encryptedKey.iv.ciphertext.tag. Um decodificador lê o header, mas o payload é texto cifrado. Solução: decodificar o payload exige a chave de descriptografia, normalmente tratada no servidor pelo seu SDK de auth, não por uma ferramenta de debug.
  3. Erro de base64url em um token que parece válido. O token foi URL-encoded em algum ponto do caminho de cópia (um cookie, uma URL de redirect, um log de proxy capturado). Você vai ver %2E ou %2B literais na string. Solução: decodifique a URL primeiro, depois jogue o resultado no decodificador JWT.
  4. Erro de parse JSON no payload. Um terminal ou cliente de chat inseriu quebras de linha de soft-wrap, ou um script colou aspas curvas em torno de um identificador. Solução: veja os bytes brutos da resposta (curl com -o file.txt, ou a visão Raw do devtools), tire os espaços em branco, cole de novo.
  5. Decodifica limpo, mas o backend continua rejeitando. Não é um problema de decodificação; é um problema de verificação. O token é estruturalmente válido; algo que o servidor confere (assinatura, aud, exp, clock skew, lookup de kid) está falhando. Pule para a próxima seção.

Duas menções honrosas que não são erros de parse mas valem capturar enquanto o decodificador está aberto: um valor alg igual a none no header (trate como hostil em produção) e um valor exp no passado (o decodificador ainda mostra as claims para você debugar, e esse é o comportamento correto; nossa ferramenta sinaliza com um badge vermelho Expirado).

Quando decodificar não basta — verificação de assinatura

Decodificar termina em “aqui está o que o token alega”. A verificação é o que transforma uma alegação em uma decisão de confiança. A assinatura é uma prova, calculada com a chave privada do emissor ou com o segredo compartilhado, que amarra header e payload: mude um único byte e a verificação falha. Sem essa checagem, qualquer pessoa que consiga fazer POST no seu endpoint pode forjar à mão um token de “admin” editando o payload e pulando a assinatura inteira.

Nunca aceite alg:none.

A verificação em produção, em qualquer linguagem e framework, se parece com este checklist. Trate itens ausentes como bugs:

  • Passe uma allowlist explícita algorithms: ['RS256'] (ou o que você usar). Isso derrota ataques de confusão de algoritmo.
  • Verifique que aud bate com o identificador do seu serviço e que iss bate com a URL de emissor esperada.
  • Confira exp contra o tempo atual com no máximo 60 segundos de tolerância de clock skew.
  • Na rotação de chaves, busque a chave pública por kid em um endpoint JWKS; nunca deixe uma chave única hardcoded para sempre.
  • Revogue de forma efetiva mantendo exp curto (minutos, não dias) e, opcionalmente, uma denylist de jti para tokens de alto valor.

Toda biblioteca JWT mainstream expõe isso como opções em uma única chamada. Se seu código de verificação não configurar tudo isso, você está deixando os defaults, e os defaults, historicamente, são o bug. Para o modelo de ameaça completo, veja nosso guia de boas práticas de segurança.

FAQ

Como decodificar um JWT sem a chave secreta?

Dá, sim. O header e o payload estão codificados em base64url, não criptografados, e qualquer pessoa com o token consegue ler suas claims. O segredo ou a chave pública só são necessários para verificar a assinatura. Isso é por design: o payload é feito para ser legível, para que o destinatário possa tomar decisões de autorização.

É seguro colar meu JWT de produção em um decodificador JWT online?

Só se o decodificador rodar no seu navegador e nunca enviar o token. Nosso Decodificador JWT faz o parse localmente com atob nativo e JSON.parse: nada é enviado a servidor nenhum. Debuggers remotos que fazem POST do seu token para uma API devem ser tratados como vazamento de credencial.

Qual a diferença entre decodificar e verificar um JWT?

Decodificar só lê as claims; não precisa de chave e não prova nada. Verificar checa a assinatura contra a chave do emissor e confirma que o token não foi adulterado. Nunca tome uma decisão de autenticação a partir de um token decodificado mas não verificado.

Meu JWT parece truncado — o que conta como formato válido?

Um JWT válido tem exatamente três segmentos base64url separados por pontos: header.payload.signature. Cinco segmentos significam que você tem um JWT criptografado (JWE), não um JWS. Zero pontos significa que você copiou só um segmento de uma linha de terminal quebrada.

Por que o decodificador ainda mostra um token expirado?

Um decodificador lê as claims independentemente da validade para você debugar rejeições. Só um verificador recusa tokens expirados. Nossa ferramenta expõe um badge Expirado comparando exp com o relógio local, para você identificar o problema na hora sem apertar os olhos em timestamps Unix.

Quais algoritmos posso decodificar?

Todos. Decodificar só precisa de base64url e parse JSON; é agnóstico quanto ao algoritmo. Isso inclui HS256/384/512, RS256/384/512, PS256/384/512, ES256/384/512, EdDSA e alg:none. Só a verificação depende do algoritmo que você escolheu.

Devo usar jwt-decode ou jsonwebtoken no Node.js?

Use jwt-decode no frontend quando você só precisa ler o payload, por exemplo para mostrar um username a partir de um access token. Use jsonwebtoken no backend, porque só o backend pode guardar a chave de assinatura e executar jwt.verify. Nunca verifique no cliente.

Conclusão

Decodificar um JWT está longe de ser tão misterioso quanto “token criptográfico” sugere. Guarde cinco pontos e você nunca mais vai ficar travado olhando para uma string opaca eyJhbGciOi…:

  • Decodificar é base64url mais parse JSON. Nenhum segredo é necessário.
  • Um JWT tem três partes (header, payload, assinatura) unidas por pontos.
  • Decodificar nunca prova autenticidade. Sempre verifique no servidor com a chave do emissor.
  • Rejeite alg:none e sempre passe uma allowlist explícita de algoritmo para verify.
  • Nunca guarde senhas, chaves privadas ou PII sensível no payload: ele é legível para quem tiver o token.

Favorite nosso decodificador JWT gratuito para debug em plantão. Cole um token, leia as claims, detecte expirações em um segundo, tudo sem seu token sair do navegador.

Artigos relacionados

Ver todos os artigos