Quando decodifico um JWT, não leio todos os claims em ordem aleatória. Uso uma sequência que me ajuda a responder rapidamente quatro perguntas: quem emitiu o token, para quem ele foi emitido, ele está dentro da janela de tempo e quais dados de autorização ele carrega?
Normalmente começo no Decodificador JWT e depois separo claims padronizados de campos específicos da aplicação.
A ordem que uso ao inspecionar o payload
Para um payload como:
{
"iss": "https://auth.example.com/",
"sub": "user_123",
"aud": "api://orders",
"scope": "orders:read orders:write",
"iat": 1789200000,
"exp": 1789203600
}
olho primeiro:
iss— quem o token declara como emissor?aud— para qual API ou destinatário ele foi emitido?expenbf— a janela temporal permite uso agora?iat— quando o token diz ter sido emitido?sub— quem ou o que é o sujeito?scope,roles,groups— quais dados de autorização aparecem?jti,azp,auth_time,acr,amr— que contexto extra o protocolo fornece?
Essa ordem me ajuda a não misturar um problema de autenticação com um problema de permissão.
iss: issuer / emissor
iss identifica o emissor declarado.
{
"iss": "https://login.example.com/"
}
Comparo esse valor com o issuer que a API foi configurada para confiar. Não confio apenas porque a URL parece conhecida.
A RFC 8725 reforça a ligação entre issuer e material criptográfico: quando o perfil usa iss, as chaves usadas para verificar o JWT precisam pertencer ao emissor que a aplicação confia.
Se suspeito de diferença, uso o Depurador JWT para comparar o valor real com o esperado e depois confirmo que o JWKS ou a chave pública estão ligados ao mesmo emissor.
aud: audience / audiência
aud responde para quem o token é destinado.
Pode ser uma string:
{
"aud": "api://orders"
}
ou uma lista:
{
"aud": ["api://orders", "api://reporting"]
}
Se a API Orders espera api://orders e o token contém api://billing, tenho uma explicação forte para um 401 mesmo que a assinatura seja válida.
Vejo esse problema com frequência quando configurações de desenvolvimento, homologação e produção se misturam. O guia de audience mismatch em JWT mostra esse fluxo em detalhe.
exp: expiration time
exp define quando o token deixa de ser aceitável por tempo.
Lembro sempre que NumericDate usa segundos, não milissegundos de JavaScript. Para converter rapidamente e ver o tempo restante, uso o Verificador de Expiração JWT.
Um exp ainda no futuro não prova que o JWT é válido. Ele apenas mostra que uma condição temporal ainda não falhou.
O guia de claim exp cobre unidade, clock skew e expiração ausente.
nbf: not before
nbf é a outra fronteira temporal: o token não deve ser aceito antes desse instante.
Quando um JWT não está expirado mas a API o rejeita, verifico se nbf está no futuro.
Também comparo relógios do emissor e do verificador, porque diferenças pequenas podem afetar tokens perto da fronteira.
iat: issued at
iat informa quando o token diz ter sido emitido.
Uso como contexto, não como regra universal de validade. Ele ajuda a responder:
- este token pertence à sessão que estou depurando?
- está inesperadamente antigo?
- a diferença entre
iateexpfaz sentido? iatestá estranhamente no futuro?
Não assumo que iat cria sozinho uma janela de expiração. Isso depende da política do sistema.
sub: subject / sujeito
sub identifica o sujeito representado pelo token.
Evito adivinhar o significado. Pode ser um ID de usuário, uma identidade de serviço ou outro identificador definido pelo emissor.
Também evito combinar valores sub de emissores diferentes como se fossem um namespace global. Muitas vezes o par issuer + subject é mais significativo do que sub isolado.
jti: JWT ID
jti identifica um JWT específico.
Pode ajudar sistemas que implementam revogação, prevenção de replay ou rastreamento, mas a mera presença de jti não cria essas propriedades automaticamente. Elas exigem política e, muitas vezes, estado no servidor.
scope, roles e groups
Esses campos são muito úteis na depuração, mas não têm uma semântica universal em todo JWT.
{
"scope": "profile orders:read",
"roles": ["support", "billing"],
"groups": ["employees"]
}
Um provedor pode usar scope separado por espaços; outro pode usar claims com namespace. Um campo chamado roles não é automaticamente autoritativo só porque está legível.
Quando autenticação funciona mas uma operação retorna 403, esses são os campos que verifico em seguida.
Claims extras em OAuth e OpenID Connect
Dependendo do provedor e do tipo de token, também posso encontrar:
azp— authorized party;auth_time— horário de autenticação;acr— authentication context class reference;amr— authentication methods references.
Só atribuo significado a esses campos quando a documentação do protocolo/provedor define como devem ser usados. Um decoder genérico consegue mostrá-los, mas não conhece todas as regras de negócio da aplicação.
A regra antes de confiar em qualquer claim
Separo três perguntas:
- Decodificar: o que o payload declara?
- Verificar: a assinatura corresponde a uma chave confiável?
- Validar: issuer, audience, tempo, tipo e permissões são aceitáveis aqui?
Nunca salto da primeira pergunta diretamente para “este usuário está autenticado”.
O Verificador de Assinatura JWT cuida da camada criptográfica suportada. O Validador JWT e o Depurador JWT ajudam a aplicar checagens de claims comuns.
Meu checklist de leitura de claims
Quando depuro um token, sigo esta ordem:
- Decodifico o JWT sem confiar nele.
- Comparo
isscom o emissor configurado. - Comparo
audcom a API atual. - Leio
exp,nbfeiatcomo uma linha do tempo. - Interpreto
subno contexto do emissor. - Só depois analiso scopes, roles e groups.
- Verifico a assinatura com uma chave ligada ao emissor confiável.
- Claims específicas do provedor são comparadas com a documentação desse provedor.
Esse processo reduz o ruído de payloads grandes. Primeiro procuro os campos que explicam confiança, destinatário, tempo e autorização; depois avanço para os detalhes da aplicação.