---
term: 'JSON Web Token (JWT)'
seoTitle: 'O que é JWT? Estrutura, Claims, Segurança e Revogação'
headline: 'O que é um JSON Web Token (JWT)?'
slug: json-web-token-jwt
category: auth-security
shortDefinition: 'Um JSON Web Token é um token compacto e seguro para URLs que carrega claims JSON assinadas, permitindo verificar requisições sem sessões no servidor.'
relatedTerms:
  - oauth-2-social-login
  - api-key-security
  - identity-access-management-iam
  - access-control-lists-acl
contrastsWith:
  - api-key-security
aboutTerms:
  - 'Claims de JWT'
  - 'JWS (JWT Assinado)'
  - 'Refresh Tokens'
faq:
  - question: 'O que é um JWT em termos simples?'
    answer: 'Uma "carteira de identidade" em JSON, assinada e codificada em Base64, que o servidor emite no login. O cliente a apresenta em cada requisição, e o servidor verifica a assinatura em vez de consultar uma sessão — o próprio token carrega quem você é e até quando. Oficialmente, a pronúncia rima com "jot", segundo a RFC que o define.'
  - question: 'Quais são as três partes de um JWT?'
    answer: 'Header (tipo do token e algoritmo de assinatura), payload (as claims — os dados JSON de fato) e assinatura, cada parte codificada em Base64Url e unida por pontos em header.payload.signature. A assinatura é computada sobre as duas primeiras partes, então qualquer adulteração nelas quebra a verificação.'
  - question: 'O JWT é criptografado?'
    answer: 'Não — codificado, não criptografado. Base64Url é um formato de transporte que qualquer um reverte; cole qualquer JWT em um decodificador e o payload fica legível. A assinatura impede adulteração, não leitura. Nunca coloque segredos ou dados pessoais sensíveis no payload de um JWT; a variante criptografada (JWE) existe para necessidades reais de confidencialidade.'
  - question: 'Qual a diferença entre JWT e session token?'
    answer: 'Onde o estado mora. Um session token é um ID opaco apontando para estado no servidor — uma consulta por requisição, revogável na hora. Um JWT carrega o estado dentro de si — sem consulta, verificável por qualquer serviço que tenha a chave, mas válido até expirar, aconteça o que acontecer. Escala stateless versus controle instantâneo.'
  - question: 'Onde devo armazenar um JWT no navegador?'
    answer: 'Não no localStorage — qualquer script injetado consegue lê-lo, transformando XSS em roubo de token. O consenso atual: mantenha o access token em memória, o refresh token em um cookie HttpOnly, Secure e SameSite, e considere o padrão backend-for-frontend, que mantém os tokens totalmente fora do navegador.'
  - question: 'Um JWT pode ser revogado?'
    answer: 'Não por design — um token assinado vale até a claim exp, o que é o preço do stateless. Os contornos reintroduzem estado: uma denylist indexada pela claim jti, chaves versionadas ou — a resposta padrão — access tokens de vida bem curta emparelhados com refresh tokens revogáveis e rotacionados.'
  - question: 'JWT é seguro?'
    answer: 'O formato é sólido; as implementações falham. Os buracos clássicos: aceitar alg none, deixar o header do token escolher o algoritmo (o ataque de confusão de RS256 para HS256), secrets HMAC curtos vulneráveis a brute force e pular a validação de aud/iss/exp. Fixe seus algoritmos, valide toda claim, mantenha vidas curtas.'
  - question: 'Qual a diferença entre JWT e OAuth?'
    answer: 'Categorias diferentes: JWT é um formato de token; OAuth 2.0 é um framework de autorização. Eles se compõem — fluxos OAuth costumam emitir access tokens que por acaso são JWTs, e os ID tokens do OpenID Connect sempre são. Um diz como o token é construído; o outro, como os tokens são distribuídos.'
codeLanguages: [javascript, dart, swift, kotlin]
externalAuthorities:
  - name: 'RFC 7519 — JSON Web Token (JWT)'
    url: 'https://datatracker.ietf.org/doc/html/rfc7519'
  - name: 'RFC 8725 — JWT Best Current Practices'
    url: 'https://datatracker.ietf.org/doc/html/rfc8725'
  - name: 'OWASP JSON Web Token Cheat Sheet'
    url: 'https://cheatsheetseries.owasp.org/cheatsheets/JSON_Web_Token_Cheat_Sheet.html'
  - name: 'RFC 7515 — JSON Web Signature (JWS)'
    url: 'https://datatracker.ietf.org/doc/html/rfc7515'
  - name: 'JSON Web Token — Wikipedia'
    url: 'https://en.wikipedia.org/wiki/JSON_Web_Token'
cta:
  title: 'Auth que já fez essas escolhas'
  text: 'O Back4app gerencia sessões com tokens revogáveis, verifica JWTs de provedores de identidade no servidor durante o login social e aplica permissões por usuário em toda requisição — higiene de tokens como comportamento de plataforma.'
  linkText: 'Comece grátis'
  linkUrl: 'https://www.back4app.com/signup'
author: 'Back4app Engineering'
publishedDate: '2026-08-20'
translationKey: json-web-token-jwt
---

**Um JSON Web Token é um token compacto e seguro para URLs que carrega claims JSON assinadas, permitindo verificar requisições sem sessões no servidor.** Esse é o enquadramento da própria [RFC 7519](https://datatracker.ietf.org/doc/html/rfc7519) — "um meio compacto e seguro para URLs de representar claims a serem transferidas entre duas partes" — e as duas ideias contidas nele carregam tudo o que vem a seguir: o token *contém* seus fatos, e uma assinatura torna esses fatos *verificáveis* por qualquer um que tenha a chave certa, sem banco de dados no caminho.

## Principais pontos

| Pergunta | Resposta |
| --- | --- |
| O formato | `header.payload.signature` — três partes Base64Url unidas por pontos |
| O truque | Qualquer um pode *decodificar*; só quem tem a chave pode *forjar* |
| Não é criptografia | O payload é legível — assinado ≠ secreto (isso é trabalho do JWE) |
| A troca | Verificação stateless ↔ nenhuma revogação nativa até o `exp` |
| A disciplina | Fixe algoritmos · valide `iss`/`aud`/`exp` · vidas curtas + rotação de refresh tokens |

## Um JWT, decodificado

```text
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 . eyJzdWIiOiJ1c3ItOGZrMiIsInJvbGUi… . dBjftJeZ4CVP…

header     { "alg": "HS256", "typ": "JWT" }
payload    { "sub": "usr-8fk2",  "role": "editor",
             "iss": "https://api.example.com",  "aud": "example-web",
             "iat": 1767024900,  "exp": 1767025800 }        ← vida de 15 minutos
signature  HMACSHA256( base64url(header) + "." + base64url(payload), secret )

Qualquer um pode DECODIFICAR as duas primeiras partes — Base64Url é empacotamento, não criptografia.
Só quem tem a chave consegue FORJAR a terceira — e esse é o truque inteiro.
```

O contraste que vale ver em código — a alternativa *stateful* contra a qual toda decisão de JWT é medida:

**JavaScript:**

```javascript
// JavaScript / Node.js — Back4app JS SDK
// The stateful contrast: Back4app issues revocable session tokens
const user = await Parse.User.logIn('ada', 'correct-horse-battery');
const token = user.getSessionToken(); // r:abc123… — opaque, server-side state
// Every request presents it; the server can revoke it instantly:
await Parse.User.logOut(); // token invalid NOW — no waiting for an exp claim
```

**Flutter:**

```dart
// Flutter / Dart — Back4app Flutter SDK
// The stateful contrast: Back4app issues revocable session tokens
final user = ParseUser('ada', 'correct-horse-battery', null);
await user.login();
final token = user.sessionToken; // r:abc123… — opaque, server-side state
// Every request presents it; the server can revoke it instantly:
await user.logout(); // token invalid NOW — no waiting for an exp claim
```

**Swift:**

```swift
// iOS / Swift — Back4app Swift SDK
// The stateful contrast: Back4app issues revocable session tokens
let user = try await User.login(username: "ada", password: "correct-horse-battery")
let token = user.sessionToken // r:abc123… — opaque, server-side state
// Every request presents it; the server can revoke it instantly:
try await User.logout() // token invalid NOW — no waiting for an exp claim
```

**Kotlin:**

```kotlin
// Android / Kotlin — Back4app Android SDK
// The stateful contrast: Back4app issues revocable session tokens
val user = ParseUser.logIn("ada", "correct-horse-battery")
val token = user.sessionToken // r:abc123… — opaque, server-side state
// Every request presents it; the server can revoke it instantly:
ParseUser.logOut() // token invalid NOW — no waiting for an exp claim
```

## Como a verificação funciona

```mermaid
flowchart LR
  accTitle: Fluxo de emissão e verificação de JWT
  accDescr: No login, o servidor assina um token contendo claims e o devolve ao cliente. O cliente o armazena e o envia como bearer token em cada requisição. O servidor verifica a assinatura com sua chave e valida as claims, aceitando ou rejeitando sem nenhuma consulta de sessão.
  L["Login<br/>(credenciais verificadas uma vez)"] --> S["Servidor assina o JWT<br/>claims + chave"]
  S --> C["Cliente guarda o token"]
  C -->|"Authorization: Bearer eyJ…"| V["Qualquer servidor com a chave:<br/>verifica a assinatura · valida as claims"]
  V -->|"válido"| OK["Requisição prossegue<br/>sem consulta de sessão"]
  V -->|"adulterado / expirado / aud errada"| NO["401"]
```

Uma precisão que os explicadores pulam: "JWT" nomeia o formato das claims; o que todo mundo de fato passa adiante é um **JWS** ([RFC 7515](https://datatracker.ietf.org/doc/html/rfc7515)) — a serialização *assinada* — enquanto o **JWE** é o irmão criptografado, para payloads que precisam permanecer ilegíveis. E a verificação são dois trabalhos, não um: checar a assinatura e depois **validar as claims** — `exp` e `nbf` contra o relógio, `iss` contra sua lista de emissores confiáveis, `aud` contra o identificador *deste serviço*. Um token perfeitamente assinado que está expirado, veio do emissor errado ou foi cunhado para outra audiência é um ataque perfeitamente assinado.

## Claims: o vocabulário do payload

| Claim | Nome | Trabalho do verificador |
| --- | --- | --- |
| `iss` | Emissor (issuer) | Este é um emissor em que eu confio? |
| `sub` | Sujeito (subject) | De quem se trata — o ID estável do usuário |
| `aud` | Audiência (audience) | Foi cunhado para *mim*? Rejeite tokens alheios |
| `exp` | Expiração | Rejeite após este timestamp Unix |
| `iat` / `nbf` | Emitido em / não antes de | Confira a sanidade da janela de validade |
| `jti` | ID do token | Identificador único — o gancho de que uma denylist precisa |
| *custom* | Papéis, tenant, plano… | Semântica do app — mínimo possível, nunca segredos |

Mantenha os payloads enxutos por dois motivos: os tokens viajam em toda requisição como headers (cookies estouram perto de 4 KB, e cada claim é banda repetida), e tudo neles é legível para quem tiver o token.

## JWT vs. session tokens

| | JWT (stateless) | Session token (stateful) |
| --- | --- | --- |
| O token é | O próprio estado, assinado | Um ponteiro opaco para estado no servidor |
| Custo por requisição | Verificação de assinatura, sem consulta | Uma consulta ao session store |
| Revogação | **Nenhuma até o `exp`** — por design | Instantânea — apague a session |
| Auth entre serviços | Qualquer serviço com a chave verifica | Os serviços precisam compartilhar o session store |
| Logout significa | O cliente descarta; o token segue válido | O token morre de verdade |
| Melhor casa | APIs, microsserviços, verificação por terceiros | Apps de backend único, sessões sensíveis |

A verdade fora de moda com que vários guias de boas práticas agora abrem: para um app renderizado no servidor com um único backend, as sessions do framework são mais simples *e* mais controláveis — JWTs pagam o próprio custo quando os tokens precisam ser verificados entre serviços ou por partes que não deveriam telefonar para casa a cada requisição.

## O problema da revogação, com honestidade

Ser stateless *é* ser incapaz de revogar — a mesma propriedade, descrita duas vezes. Um token assinado é válido até o `exp` não importa o que tenha acontecido desde então: logout, troca de senha, banimento da conta. Toda correção reintroduz estado, então escolha o sabor: uma **denylist** indexada por `jti` + `iss` (a construção [recomendada pela OWASP](https://cheatsheetseries.owasp.org/cheatsheets/JSON_Web_Token_Cheat_Sheet.html)) checada a cada requisição — estado pequeno, mas estado; **versionamento de chaves**, que revoga *todo mundo* de uma vez; ou a arquitetura padrão — **access tokens de vida curta (5–15 minutos) mais refresh tokens revogáveis** com rotação: cada refresh emite um novo refresh token e aposenta o anterior, então um token roubado morre no primeiro replay, e o reúso de um token aposentado sinaliza o roubo e mata a família inteira de tokens. A latência de revogação passa a ser igual à vida do access token — que é a verdadeira razão de essas vidas serem curtas.

## Escolhendo o algoritmo — e os ataques contra a escolha

| | HS256 (HMAC) | RS256 (RSA) | EdDSA / ES256 |
| --- | --- | --- | --- |
| Chaves | Um secret compartilhado | A privada assina, a pública verifica | A privada assina, a pública verifica |
| Verificadores precisam | Do *secret* (que também forja!) | Só da chave pública | Só da chave pública |
| Serve para | Emissor = verificador, uma só parte | Multisserviço, terceiros | Idem, assinaturas menores e mais rápidas |
| Aresta afiada | Secrets curtos sofrem brute force offline a partir de um único token capturado — use ≥256 bits aleatórios | Tokens maiores, mais lento | Suporte de biblioteca mais recente |

A seção de ataques para a qual a [RFC 8725](https://datatracker.ietf.org/doc/html/rfc8725) existe, em três frases. `alg: "none"` é um valor legal de header — bibliotecas que o honram aceitam tokens sem assinatura. O ataque de confusão: um verificador que deixa o *header do token* escolher o algoritmo pode receber um token "HS256" assinado com a chave RSA *pública* do servidor usada como secret do HMAC — um valor público usado como privado. Os dois morrem do mesmo jeito: **o verificador fixa em configuração os algoritmos e as chaves que aceita e nunca confia no header para escolher.**

## Onde armazenar JWTs no navegador

| Local | XSS rouba? | CSRF envia? | Sobrevive ao refresh da página? | Veredito |
| --- | --- | --- | --- | --- |
| localStorage | **Sim** | Não | Sim | Evite — legível por script |
| Cookie comum | Sim (legível por script) | **Sim** | Sim | O pior dos dois mundos |
| Cookie HttpOnly + Secure + SameSite | Não | Mitigado pelo SameSite | Sim | Bom — para o refresh token |
| Em memória | Só enquanto o script injetado vive | Não | Não | Bom — para o access token |
| Backend-for-frontend guarda os tokens | O navegador nunca os tem | Valem as regras de cookie | Sim | O mais forte para SPAs |

O modelo de ameaça, não o folclore: localStorage troca imunidade a CSRF por roubo via XSS, cookies trocam o inverso — e é por isso que o consenso divide o par: access token em memória, refresh token em um cookie endurecido.

## Casos de uso comuns

- **Autenticação de APIs** — o bearer token por trás dos headers `Authorization` em APIs [REST](/glossary/pt/api-rest/) e GraphQL.
- **Identidade em microsserviços** — um token emitido pelo gateway e verificado de forma independente por cada serviço, sem session store compartilhado.
- **ID tokens do OpenID Connect** — a asserção de identidade assinada em todo [login social](/glossary/oauth-2-social-login/) — sempre um JWT.
- **Handoffs entre sistemas** — links assinados, payloads de webhook, concessões de download: claims verificadas por uma parte que não pode te chamar de volta.
- **Dicas de autorização stateless** — papéis e IDs de tenant carregados em claims, com a verificação autoritativa ainda no servidor.

## JWTs ou sessions? Matriz de decisão

| Sua situação | Escolha |
| --- | --- |
| Backend único, app renderizado no servidor | Sessions — mais simples, revogáveis na hora |
| API pública consumida por muitos serviços | JWTs, chaves assimétricas |
| Microsserviços atrás de um gateway | JWTs — verificação local, sem store compartilhado |
| Bloqueio instantâneo é requisito inegociável | Sessions, ou JWTs + denylist e `exp` curto |
| Terceiros precisam verificar suas asserções | JWTs — o jogo em casa do formato |
| App mobile contra um BaaS | O mecanismo de session da plataforma — ela já escolheu |

## Limitações e trade-offs

- **A irrevogabilidade é estrutural.** Toda mitigação — denylists, vidas curtas, rotação — é uma devolução parcial do estado que você removeu; precifique isso antes de escolher stateless.
- **Claims envelhecem.** Os papéis são um snapshot da emissão; um admin rebaixado continua admin até expirar. Vidas curtas limitam a janela de defasagem.
- **O payload é público.** Trate-o como legível pelo usuário e por qualquer ladrão de token — identificadores sim, segredos e PII não.
- **Defaults de biblioteca já queimaram muita gente.** Fixar algoritmos, validar claims e checar o `typ` são responsabilidades da sua configuração, não garantias da biblioteca.
- **O tamanho se acumula.** Cada claim viaja em cada requisição; payloads generosos taxam a banda mobile e podem estourar os limites de cookie.

## Tokens no Back4app

O Back4app é uma plataforma open-source de Backend as a Service (BaaS) que combina banco de dados gerenciado, APIs REST e GraphQL geradas automaticamente, autenticação, armazenamento de arquivos e funções serverless com Cloud Code. A escolha da própria plataforma ilustra a matriz de decisão: as sessões de cliente usam **session tokens revogáveis, mantidos no servidor** — as abas de código acima — então logout, trocas de senha e o encerramento de sessões pelo dashboard têm efeito imediato, sem janela de expiração para esperar. JWTs aparecem onde pertencem: os ID tokens OIDC dos provedores de identidade são verificados no servidor pelos adaptadores de auth durante o [login social](/glossary/oauth-2-social-login/), e funções de Cloud Code podem cunhar ou verificar JWTs para handoffs com terceiros usando bibliotecas padrão — com o secret de assinatura na configuração do servidor, nunca enviado aos clientes. Stateful onde o controle importa, stateless nas bordas onde a verificação precisa viajar: a arquitetura que este artigo defende, já montada.
