JWT: o token que carrega a própria assinatura
Um JWT não esconde nada de quem o lê — ele só prova que ninguém alterou o que está escrito ali.
JWT (JSON Web Token) é o formato mais comum de bearer token hoje. A diferença dele pra um token aleatório qualquer (como o ID de sessão da lição 2) é que um JWT carrega dados legíveis dentro de si mesmo — quem é o usuário, quando o token expira — e uma assinatura que prova que ninguém alterou esses dados no caminho. Isso muda completamente como ele deve ser tratado, e onde ele quebra.
As três partes, separadas por ponto
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjMiLCJyb2xlIjoidXN1YXJpbyIsImV4cCI6MTc1ODIwMDAwMH0.k3f9A1z...
╰──────── header ────────╯╰──────────── payload ────────────╯╰── assinatura ──╯// header — qual algoritmo foi usado pra assinar
{ "alg": "HS256" }
// payload — os dados de fato ("claims")
{ "sub": "123", "role": "usuario", "exp": 1758200000 }header e payload são só JSON convertido pra Base64 — não são criptografados. A terceira parte, a assinatura, é o resultado de rodar um algoritmo (HMAC-SHA256, no exemplo acima) sobre o header e o payload combinados, usando uma chave secreta que só o servidor conhece. É essa assinatura que garante que, se um byte do payload mudar, o resultado do algoritmo muda junto — e deixa de bater com a assinatura original.
"Assinado, não criptografado" — o que isso muda na prática
Qualquer pessoa com o token em mãos consegue decodificar o header e o payload sozinha, sem chave nenhuma — é só Base64, o mesmo que colar num decodificador online. O que ninguém sem a chave secreta consegue fazer é gerar uma assinatura válida pra um payload alterado. É por isso que um JWT nunca deve carregar dado sensível no payload (senha, número de cartão) — ele não está escondido, só está assinado.
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjMiLCJyb2xlIjoiYWRtaW4ifQ.k3f9A1z...
// alguém editou "role":"usuario" pra "role":"admin" no payload,
// mas manteve a MESMA assinatura antiga (não tem como recalcular
// sem a chave secreta)
// servidor recalcula o HMAC do novo payload e compara:
// resultado NÃO bate com "k3f9A1z..." → 401, token rejeitadoAuthorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjMiLCJyb2xlIjoidXN1YXJpbyJ9.k3f9A1z...
// payload intacto, exatamente como o servidor emitiu
// servidor recalcula o HMAC e compara:
// resultado bate com "k3f9A1z..." → token aceitoNote que o atacante do exemplo "ruim" nem precisa entender criptografia pra tentar o ataque — o Base64 é trivial de editar. O que impede o ataque é só uma coisa: ele não tem a chave secreta pra gerar uma nova assinatura válida pro payload que ele acabou de alterar.
exp — o token tem prazo de validade embutido
O campo exp ("expiration") guarda um timestamp; o servidor confere esse valor a cada requisição e rejeita qualquer token com exp no passado, mesmo que a assinatura continue perfeitamente válida. É comum ver tokens de acesso com vida curta (minutos a poucas horas) acompanhados de um segundo token, o refresh token, guardado de forma mais protegida e usado só pra pedir um token de acesso novo sem repetir login inteiro — mas o mecanismo de expiração em si é sempre o mesmo campo exp.
Por que revogar um JWT antes do exp é difícil
Aqui está a diferença mais importante em relação à sessão por cookie da lição 2. Lá, "derrubar uma sessão" era apagar uma linha numa tabela — o servidor consultava aquela tabela a cada requisição de qualquer jeito. Um JWT foi desenhado pra ser stateless: o servidor não guarda uma lista de "tokens ativos" pra consultar, ele só reconfere a assinatura matematicamente a cada requisição. Isso é ótimo pra escalar (nenhum banco é consultado só pra validar identidade) — mas significa que não existe um "apagar esse token" de verdade antes do exp chegar. Se um JWT vazar, ele continua válido até expirar sozinho, a não ser que a aplicação implemente uma lista de revogação separada (voltando a depender de uma consulta central, o que anula parte do ganho de ser stateless).
Exercício
Um time decide guardar { "saldo_conta": 15000 } dentro do payload de um JWT, pra evitar uma consulta ao banco a cada requisição. Explique por que isso é perigoso, mesmo o token estando assinado corretamente.