---
author: Jan Brennenstuhl
canonical_url: https://www.janbrennenstuhl.eu/knowledge-base/oidc/jwt-anatomy/
description: The 3 parts of a signed JSON Web Token, what each one contains, and why reading a token is not the same as trusting it.
id: jb-guide-jwt-anatomy
kind: guide
license:
  name: CC BY 4.0
  scope: Original text; credited third-party media retain their own rights.
  url: https://creativecommons.org/licenses/by/4.0/
markdown_url: https://www.janbrennenstuhl.eu/knowledge-base/oidc/jwt-anatomy/index.md
modified: '2026-10-13T00:00:00+02:00'
published: '2026-10-13T00:00:00+02:00'
review_basis: ''
review_status: not-reviewed
reviewed: ''
series: ''
status: published
title: What is inside a JWT?
topics:
- tokens-jwt
- identity-authentication
---

# What is inside a JWT?

**Short answer:** A signed JWT is 3 base64url-encoded parts joined by dots: a header that names the algorithm, a payload of claims, and a signature over both. The payload is encoded, not encrypted, so anyone holding the token can read it; only a signature check with a key you trust tells you whether to believe it.


<a id="key-findings"></a>
## Key findings


<a id="f-jwt-claims-in-jws-or-jwe"></a>
> **Finding f-jwt-claims-in-jws-or-jwe:** A JWT is a set of claims encoded as a JSON object inside a JWS or a JWE, so the same token format can be signed, encrypted, or both.
>
> Source: [RFC 7519, JSON Web Token (JWT), Section 2](https://www.rfc-editor.org/rfc/rfc7519#section-2)
> Link: https://www.janbrennenstuhl.eu/knowledge-base/oidc/jwt-anatomy/#f-jwt-claims-in-jws-or-jwe



<a id="f-jwt-payload-is-readable"></a>
> **Finding f-jwt-payload-is-readable:** The payload of a signed JWT is only base64url-encoded, so a token that carries privacy-sensitive claims needs encryption or must not reach parties who should not read it.
>
> Source: [RFC 7519, Section 12, Privacy Considerations](https://www.rfc-editor.org/rfc/rfc7519#section-12)
> Link: https://www.janbrennenstuhl.eu/knowledge-base/oidc/jwt-anatomy/#f-jwt-payload-is-readable



<a id="f-jwt-registered-claims-optional"></a>
> **Finding f-jwt-registered-claims-optional:** RFC 7519 registers 7 claim names, iss, sub, aud, exp, nbf, iat and jti, and none of them is mandatory in every case; each application profile decides which it requires.
>
> Source: [RFC 7519, Section 4.1](https://www.rfc-editor.org/rfc/rfc7519#section-4.1)
> Link: https://www.janbrennenstuhl.eu/knowledge-base/oidc/jwt-anatomy/#f-jwt-registered-claims-optional



<a id="f-jwt-allowlist-algorithms"></a>
> **Finding f-jwt-allowlist-algorithms:** JWT libraries must let the caller specify the supported algorithms and must not use any other algorithm, which closes the door on tokens whose header was changed to "none".
>
> Source: [RFC 8725, JSON Web Token Best Current Practices, Section 3.1](https://www.rfc-editor.org/rfc/rfc8725#section-3.1)
> Link: https://www.janbrennenstuhl.eu/knowledge-base/oidc/jwt-anatomy/#f-jwt-allowlist-algorithms


<a id="evidence"></a>
## Evidence


**Specifications**

| Study | Year | Sample | Method | Finding |
| --- | --- | --- | --- | --- |
| [RFC 7519: JSON Web Token (JWT)](https://www.rfc-editor.org/rfc/rfc7519) (IETF) | 2015 | n/a | Specification | Defines the JWT format, the registered claim names and the privacy considerations for claims. |

| [RFC 7515: JSON Web Signature (JWS)](https://www.rfc-editor.org/rfc/rfc7515) (IETF) | 2015 | n/a | Specification | Defines the compact serialization: base64url header, payload and signature joined by dots. |

| [RFC 8725: JSON Web Token Best Current Practices](https://www.rfc-editor.org/rfc/rfc8725) (IETF (BCP 225)) | 2020 | n/a | Best current practice | Requires algorithm allowlists in libraries and audience, issuer and key validation in applications. |


<a id="what-to-do"></a>
## What to do

<a id="for-engineers"></a>
### For engineers

- Treat a decoded payload as untrusted input until the signature verifies. Pin the algorithms you accept instead of reading `alg` from the token, and resolve the key by `kid` only from a key set you trust, such as the issuer's [JWKS](https://www.janbrennenstuhl.eu/jwks/).
- After the signature, check `exp`, `nbf`, `iss` and `aud`. RFC 8725 makes audience validation a must whenever one issuer serves several relying parties.
- Keep secrets and personal data out of signed tokens. If a token has to carry them, use [JWE](https://www.janbrennenstuhl.eu/jwe/) or keep the data on the server and put a reference in the token.

<a id="for-product-and-ux"></a>
### For product and UX

- Tokens end up in logs, browser storage and support tickets. Anything in the payload should be safe to show to whoever might see it there.
- Short token lifetimes are a product decision as much as a security one: they decide how often a session has to be refreshed.

<a id="try-it"></a>
## Try it


<a id="ex-jwt"></a>
**Decode a token in your browser** (interactive on the web page; static explanation follows)

The example token below has 3 parts. Paste your own token into the decoder to split and decode it. Decoding happens only in your browser; nothing is sent anywhere, and the signature is not verified.

`eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjIwMjYtMTAifQ.eyJpc3MiOiJodHRwczovL2lkLmV4YW1wbGUuY29tIiwic3ViIjoidXNlci0xMjMiLCJhdWQiOiJodHRwczovL2FwaS5leGFtcGxlLmNvbSIsImV4cCI6MTc5MTkwMDAwMCwiaWF0IjoxNzkxODk2NDAwLCJqdGkiOiI3ZjNjOWEifQ.cGxhY2Vob2xkZXIgc2lnbmF0dXJlLCBub3QgdmFsaWQ`

- **Header** decodes to `{"alg":"ES256","typ":"JWT","kid":"2026-10"}`: an ECDSA P-256 signature, and the ID of the key that made it.
- **Payload** decodes to `{"iss":"https://id.example.com","sub":"user-123","aud":"https://api.example.com","exp":1791900000,"iat":1791896400,"jti":"7f3c9a"}`: issued at 13:00 UTC on 13 October 2026, expiring 1 hour later.
- **Signature** is a placeholder in this example. In a real token it is the ES256 signature over the encoded header and payload, checked with the issuer's public key from its JWKS.


<a id="how-the-parts-fit-together"></a>
## How the parts fit together

The header and payload are JSON objects. Each is encoded with base64url, a URL-safe variant of base64 without padding, and the two results are joined with a dot. The signer signs that string with the algorithm named in the header and appends the base64url-encoded signature after a second dot. That is the JWS compact serialization from RFC 7515.

A verifier reverses the steps: split at the dots, decode the header, pick the key and algorithm it allows, and verify the signature over the first 2 parts exactly as received. Only then does it decode the payload and evaluate the claims. The essays on [the purpose of JWT](https://www.janbrennenstuhl.eu/jwt-stateless-authentication/), [JSON Web Algorithms](https://www.janbrennenstuhl.eu/jwa/) and [JWT versus OAuth 2.0](https://www.janbrennenstuhl.eu/jwt-vs-oauth/) go deeper.

<a id="related"></a>
## Related

- [How does OAuth 2.0 Token Exchange keep provider tokens away from agents?](https://www.janbrennenstuhl.eu/knowledge-base/agent-identity/token-exchange/)
- [Where do OpenID Connect implementations break?](https://www.janbrennenstuhl.eu/knowledge-base/oidc/openid-connect-security/)


---

Cite as: Jan Brennenstuhl. "What is inside a JWT?." Brennenstuhl on Security, 13 October 2026. https://www.janbrennenstuhl.eu/knowledge-base/oidc/jwt-anatomy/
License: CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/) for the original text; credited third-party media keep their own rights.
