# Como decodificar e verificar um JWT em JavaScript e Python

> Decodificar um JWT é só ler o header e o payload, que são dados em Base64URL e podem ser lidos por qualquer pessoa. Verificar é conferir a assinatura com a chave, e só isso prova que o token é autêntico. Este guia mostra as duas coisas em JavaScript e Python, com os cuidados que evitam falhas de segurança.

- Endereço oficial: https://pradev.com.br/guias/como-decodificar-e-verificar-jwt
- Tipo: Guia
- Publicado em: 2026-09-30
- Atualizado em: 2026-09-30
- Autor: Leonardo Gaertner (https://pradev.com.br/sobre)
- Ferramentas: [Decodificador JWT](https://pradev.com.br/decodificador-jwt), [Gerador de HMAC](https://pradev.com.br/gerador-de-hmac), [Decodificar Base64](https://pradev.com.br/decodificar-base64)

## As três partes de um JWT

Um JWT tem três partes separadas por ponto: header, payload e assinatura. As duas primeiras são objetos JSON codificados em Base64URL, uma variação do Base64 que usa - e _ no lugar de + e / e dispensa o preenchimento com =. Você pode ler qualquer uma delas com o [decodificador de Base64](https://pradev.com.br/decodificar-base64). A terceira é a assinatura.

- Header: o algoritmo e o tipo, como {"alg":"HS256","typ":"JWT"}.
- Payload: os dados, chamados de claims, como sub, iat e exp.
- Assinatura: a prova de que o header e o payload não foram alterados.

Você pode ver as três partes de qualquer token no [decodificador de JWT](https://pradev.com.br/decodificador-jwt).

## Decodificar um JWT em JavaScript

Decodificar não exige chave nenhuma, porque o conteúdo do token não é secreto.

```javascript
function decodificarJWT(token) {
  const [header, payload] = token.split('.');

  const decodificar = (parte) => {
    // Base64URL usa - e _ no lugar de + e /, e não tem o preenchimento com =
    const base64 = parte.replace(/-/g, '+').replace(/_/g, '/');
    const binario = atob(base64.padEnd(Math.ceil(base64.length / 4) * 4, '='));
    const bytes = Uint8Array.from(binario, (c) => c.charCodeAt(0));
    return JSON.parse(new TextDecoder().decode(bytes));
  };

  return { header: decodificar(header), payload: decodificar(payload) };
}
```

_JavaScript. Funciona no navegador e no Node.js, e entende acentos no payload._

Atenção: isso só lê o token. Nunca use o resultado para decidir se alguém está autorizado, porque qualquer pessoa consegue fabricar um token com o conteúdo que quiser.

## Verificar a assinatura em Node.js (HS256)

No HS256, a assinatura é um HMAC-SHA256 do texto `header.payload`, calculado com um segredo compartilhado (você pode reproduzir esse cálculo no [gerador de HMAC](https://pradev.com.br/gerador-de-hmac)). Verificar é recalcular o HMAC e comparar:

```javascript
import { createHmac, timingSafeEqual } from 'node:crypto';

function verificarHS256(token, segredo) {
  const [header, payload, assinatura] = token.split('.');

  // Aceite só o algoritmo que você espera, nunca o que o token diz
  const cabecalho = JSON.parse(Buffer.from(header, 'base64url').toString());
  if (cabecalho.alg !== 'HS256') throw new Error('Algoritmo não aceito');

  const esperada = createHmac('sha256', segredo).update(`${header}.${payload}`).digest();
  const recebida = Buffer.from(assinatura, 'base64url');
  if (recebida.length !== esperada.length || !timingSafeEqual(recebida, esperada)) {
    throw new Error('Assinatura inválida');
  }

  const dados = JSON.parse(Buffer.from(payload, 'base64url').toString());
  if (dados.exp && dados.exp * 1000 <= Date.now()) throw new Error('Token expirado');
  return dados;
}
```

_JavaScript_

A função faz as quatro coisas que não podem faltar:

- Aceita só o algoritmo esperado, e não o que o token declara.
- Recalcula a assinatura com o segredo.
- Compara em tempo constante, com `timingSafeEqual`.
- Confere a expiração (`exp`) depois de validar a assinatura.

Em produção, prefira uma biblioteca consolidada, como a `jose`, que também cobre outros algoritmos e as demais regras de validação.

## Verificar a assinatura em Python

A mesma verificação, com a biblioteca padrão:

```python
import base64
import hashlib
import hmac
import json
import time


def base64url(parte: str) -> bytes:
    return base64.urlsafe_b64decode(parte + "=" * (-len(parte) % 4))


def verificar_hs256(token: str, segredo: str) -> dict:
    header, payload, assinatura = token.split(".")

    # Aceite só o algoritmo que você espera, nunca o que o token diz
    if json.loads(base64url(header)).get("alg") != "HS256":
        raise ValueError("Algoritmo não aceito")

    esperada = hmac.new(segredo.encode(), f"{header}.{payload}".encode(), hashlib.sha256).digest()
    if not hmac.compare_digest(esperada, base64url(assinatura)):
        raise ValueError("Assinatura inválida")

    dados = json.loads(base64url(payload))
    if "exp" in dados and dados["exp"] <= time.time():
        raise ValueError("Token expirado")
    return dados
```

_Python_

## O que conferir além da assinatura

- `exp`: o token não expirou.
- `nbf`: o token já pode ser usado, quando o campo existir.
- `iss`: quem emitiu é quem você espera.
- `aud`: o token foi feito para a sua aplicação.
- O algoritmo: aceite uma lista fixa, nunca `none` nem o que vier no header.

## Erros que causam falhas de segurança

- Aceitar `alg: none`: códigos antigos aceitavam tokens sem assinatura quando o header pedia. Fixe o algoritmo no servidor.
- Colocar dados sigilosos no payload: ele pode ser lido por qualquer pessoa.
- Comparar assinaturas com `==`: use uma comparação em tempo constante.
- Guardar o token onde scripts da página conseguem ler (como o localStorage) em aplicações expostas a XSS: cookies `HttpOnly` reduzem esse risco.

## Perguntas frequentes

### Decodificar um JWT é o mesmo que verificar?

Não. Decodificar só lê o conteúdo, que não é secreto. Verificar confere a assinatura com a chave e é o que prova que o token é autêntico. Só se deve confiar nos dados de um token depois de verificado.

### O JWT é criptografado?

Em geral, não. O JWT mais comum é apenas assinado (JWS): o conteúdo é legível por qualquer pessoa, e a assinatura só garante que ele não foi alterado. Por isso não se deve colocar senhas ou dados sigilosos no payload.

### Qual a diferença entre HS256 e RS256?

No HS256, a mesma chave secreta assina e verifica, então quem verifica também poderia assinar. No RS256, a assinatura usa uma chave privada e a verificação usa a chave pública, o que permite que vários serviços verifiquem tokens sem poder emiti-los.

### Posso colar um token de produção no decodificador do PraDev?

O decodificador roda no navegador e não envia o token a nenhum servidor, mas um token ativo funciona como uma senha temporária. Prefira tokens de teste ou já expirados.

## Fontes

- [RFC 7519: JSON Web Token (JWT)](https://www.rfc-editor.org/rfc/rfc7519)

## Guias relacionados

- [Como validar a assinatura de um webhook com HMAC](https://pradev.com.br/guias/como-validar-assinatura-de-webhook-hmac): Aprenda a conferir a assinatura HMAC-SHA256 de webhooks em Node.js, Python e PHP, com código testado e os erros que fazem a validação falhar.
