Decodificador de JWT
Lê o cabeçalho e o payload de um JSON Web Token, traduz as datas e diz se ele expirou. E faz isso inteiramente no seu navegador — o que aqui não é conveniência, é o ponto: um JWT é uma credencial, e colar um token de produção num decodificador online é entregar a sessão a quem hospeda aquele site.
Leitor de token
Cabeçalho
Payload
Claims
| Claim | Valor | Como data |
|---|
As claims registradas, e o que fazer com cada uma
| Claim | Significado | Observação |
|---|---|---|
| iss | Emissor — quem gerou o token | Valide sempre. Aceitar token de qualquer emissor é o mesmo que não validar. |
| sub | Sujeito — de quem é o token | 🔴 No .NET vira ClaimTypes.NameIdentifier por padrão. Ver a seção do remapeamento. |
| aud | Destinatário — para qual serviço vale | Valide. Sem isso, token emitido para outra API é aceito na sua. |
| exp | Expira em (segundos desde 1970) | Token sem exp NUNCA expira. É achado de segurança, não descuido. |
| nbf | Não vale antes de | Usado para emitir token com início programado. |
| iat | Emitido em | Serve para medir idade do token, não para expirar. |
| jti | Identificador único do token | Base para revogação: só dá para invalidar um token se ele tiver identidade. |
| scope | Permissões concedidas | Não é padrão do JWT — é do OAuth 2. Formato varia entre provedores. |
| azp | Cliente autorizado | Comum no Keycloak e no Auth0. |
Lendo um JWT em C#
// O handler ATUAL. O JwtSecurityTokenHandler, de System.IdentityModel.Tokens.Jwt,
// é a geração anterior — ainda funciona, mas é mais lento e é ele que faz o
// remapeamento de claim que confunde todo mundo (ver abaixo).
using Microsoft.IdentityModel.JsonWebTokens;
var handler = new JsonWebTokenHandler();
var token = handler.ReadJsonWebToken(valor);
var sub = token.GetClaim("sub").Value;
var exp = token.ValidTo; // já em DateTime UTCPor que User.FindFirst("sub") devolve null
Esta é a armadilha que mais custa tempo em ASP.NET Core, e ela não dá erro: dá null. O pipeline de autenticação traduz as claims do token para as URIs longas do WS-Federation antes de você vê-las. Você inspeciona o token — aqui mesmo, nesta página —, vê sub, escreve sub no código, e recebe nada.
// 🔴 A armadilha que mais custa tempo em ASP.NET Core.
//
// Por padrão o pipeline traduz as claims do JWT para as URIs longas do WS-Federation:
// "sub" -> "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier"
// "email" -> "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"
// "role" -> "http://schemas.microsoft.com/ws/2008/06/identity/claims/role"
//
// O sintoma: você inspeciona o token, vê "sub", escreve o código abaixo e recebe null.
var id = User.FindFirst("sub")?.Value; // null!
var id = User.FindFirst(ClaimTypes.NameIdentifier)?.Value; // funciona
// Para manter os nomes originais e parar de adivinhar:
builder.Services.AddAuthentication().AddJwtBearer(opcoes =>
{
opcoes.MapInboundClaims = false; // <- a linha que resolve
opcoes.TokenValidationParameters.NameClaimType = "name";
opcoes.TokenValidationParameters.RoleClaimType = "role";
});A tolerância de relógio que ninguém espera
Se o seu token expirou e a API continua aceitando, quase sempre é isto: o ClockSkew do TokenValidationParameters tem padrão de cinco minutos. A intenção é absorver diferença de relógio entre quem emite e quem valida — o efeito colateral é que o teste de expiração parece não funcionar.
opcoes.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = "https://seu-emissor",
ValidateAudience = true,
ValidAudience = "sua-api",
ValidateLifetime = true,
// 🔴 O padrão é CINCO MINUTOS de tolerância. Um token "expirado" continua
// sendo aceito por até 5 min, e é por isso que o teste de expiração
// "não funciona" para quem não sabe disso. Zere se precisar de precisão.
ClockSkew = TimeSpan.FromMinutes(1),
ValidateIssuerSigningKey = true,
IssuerSigningKey = chave
};Perguntas frequentes
O token que eu colo aqui é enviado para algum servidor?
Não, e nesta ferramenta isso é mais importante do que em qualquer outra do site. Um JWT é uma credencial: quem tem o token age como o usuário até ele expirar. Colar um token de produção num decodificador online é entregar a sessão a quem hospeda aquele site. Aqui a leitura acontece inteiramente no seu navegador — e o segredo que você digitar para conferir a assinatura também não sai daqui. Dá para verificar abrindo o DevTools na aba Network: nenhuma requisição parte quando você cola o token.
JWT é criptografado? Posso guardar dado sensível no payload?
Não e não. O payload de um JWT é apenas codificado em base64url, que é uma representação, não uma cifra — qualquer pessoa com o token lê o conteúdo sem precisar de chave nenhuma, exatamente como esta página acabou de fazer. A assinatura garante que o conteúdo não foi ALTERADO, não que ele seja secreto. Nunca coloque senha, número de documento, dado de cartão ou qualquer informação sensível no payload. Se o conteúdo precisa ser secreto, o que se usa é JWE, que é outra coisa.
Por que User.FindFirst("sub") devolve null no ASP.NET Core?
Porque o pipeline de autenticação remapeia as claims do token para as URIs longas do WS-Federation antes de você vê-las. A claim sub vira ClaimTypes.NameIdentifier, email vira ClaimTypes.Email e role vira a URI de role da Microsoft. Você inspeciona o token, vê o nome curto, escreve o nome curto no código e recebe null. Para manter os nomes originais, defina MapInboundClaims como false na configuração do JwtBearer, e ajuste NameClaimType e RoleClaimType para os nomes que o seu emissor usa.
Meu token expirou mas a API continua aceitando. Por quê?
Provavelmente pela tolerância de relógio. O TokenValidationParameters do .NET tem ClockSkew com padrão de CINCO MINUTOS, pensado para absorver diferença de relógio entre o emissor e quem valida. Na prática isso significa que um token vale até cinco minutos além do exp, e é a explicação para o teste de expiração que parece não funcionar. Se você precisa de expiração exata, reduza ou zere o ClockSkew — sabendo que aí passa a depender de os relógios estarem sincronizados.
O que é o ataque do alg:none?
É a tentativa mais antiga contra JWT: o atacante pega um token válido, altera o payload como quiser, troca o algoritmo do cabeçalho para none e remove a assinatura. Uma biblioteca que confie no campo alg do próprio token para decidir como validar aceita o resultado como legítimo. A defesa é não deixar o token escolher: configure quais algoritmos são aceitos na validação e recuse o resto. Esta ferramenta sinaliza alg none explicitamente quando encontra.
Qual a diferença entre JwtSecurityTokenHandler e JsonWebTokenHandler?
São duas gerações da mesma biblioteca. O JwtSecurityTokenHandler, do pacote System.IdentityModel.Tokens.Jwt, é o antigo, e é ele que carrega o mapa de remapeamento de claims. O JsonWebTokenHandler, do Microsoft.IdentityModel.JsonWebTokens, é o atual: mais rápido, com menos alocação, e é o que o ASP.NET Core usa por padrão no JwtBearer a partir do .NET 8. Em código novo, prefira o JsonWebTokenHandler.
Esta ferramenta valida a assinatura?
Valida as assinaturas HMAC — HS256, HS384 e HS512 — se você informar o segredo compartilhado, e o segredo não sai do seu navegador. Não valida RS256, ES256 e as demais assimétricas, porque elas exigem a chave pública do emissor em formato JWK ou PEM, o que é outro fluxo. Vale notar que decodificar e validar são coisas diferentes: a leitura do payload nunca prova que o token é legítimo.
Outras ferramentas
Todas em ferramentas para desenvolvedor — rodam no navegador, sem cadastro e sem anúncio.
Receba os próximos artigos
Conteúdo técnico de .NET direto no seu e-mail. Sem spam, e você sai quando quiser.