JWT Structure
A JSON Web Token (RFC 7519) is three base64url-encoded segments separated by dots. Each segment serves a specific purpose.
The three segments
header.payload.signature
# Header (decoded):
{"alg":"HS256","typ":"JWT","kid":"signing-key-2026-05"}
# Payload (decoded):
{
"iss": "https://issuer.example",
"sub": "user_42",
"aud": "api.example",
"exp": 1700003600,
"iat": 1700000000,
"scope": "read:profile write:profile"
}
# Signature:
# HMAC-SHA256(secret, base64url(header) + "." + base64url(payload))
# or RSA / ECDSA signature for asymmetric algorithms
Standard claims
| Claim | Name | Use |
|---|---|---|
iss | Issuer | Who issued the token. |
sub | Subject | The principal the token is about. |
aud | Audience | Intended recipients. |
exp | Expires at | Unix time after which to reject. |
nbf | Not before | Unix time before which to reject. |
iat | Issued at | Unix time of issue. |
jti | JWT ID | Unique ID — useful for revocation. |
Algorithms you'll see
- HS256 / HS384 / HS512 — HMAC with SHA. Symmetric. Same key signs and verifies. Simple, fast; suitable when one party owns both ends.
- RS256 / RS384 / RS512 — RSA signatures. Asymmetric. Anyone with the public key can verify; only the private key can sign.
- ES256 / ES384 / ES512 — ECDSA. Asymmetric, smaller signatures, faster than RSA at the same security level.
- EdDSA — Ed25519 signatures. The modern default for new designs.
- none — claims that the token is unsigned. Reject it. Historic vulnerability vector.
!
Algorithm-confusion attack. If a server is configured to accept multiple algorithms with the same key material, an attacker can swap RS256 (which expects a public key) for HS256 (which uses a secret) — and craft tokens signed with the public key as if it were the HMAC secret. Always whitelist a specific algorithm in your verifier.
Things to do
- Verify the signature with a hard-coded algorithm.
- Validate
iss,aud, andexpon every request. - Keep token lifetimes short. Use refresh tokens for sessions.
- Rotate signing keys with a
kidheader that points at the current key. - Send tokens in the
Authorizationheader, not in URLs or non-HttpOnly cookies.