askmeidentity · JWT structure · 2026-05-22
JWT structure — anatomy + claims
A printable single-page reference for JSON Web Tokens — the three base64url segments, all RFC 7519 registered claims, common signing algorithms, validation must-checks, and the most common JWT attack vectors.
The three segments
| Header | base64url JSON | Typ, alg, kid (key id), jku (JWK URL) |
| Payload | base64url JSON | Claims — registered + private |
| Signature | base64url bytes | HMAC or asymmetric signature over header.payload |
Registered claims (RFC 7519)
| iss | string | Issuer — who minted the token |
| sub | string | Subject — typically user ID |
| aud | string | array | Audience — who can consume the token |
| exp | NumericDate | Expiration — past this, token is invalid |
| nbf | NumericDate | Not Before — token not valid until this time |
| iat | NumericDate | Issued At — when the token was minted |
| jti | string | JWT ID — unique identifier for revocation |
Common signing algorithms
| HS256 | HMAC + SHA-256 | Symmetric. Shared secret. OK for internal services. |
| RS256 | RSA + SHA-256 | Asymmetric. Most common for OIDC ID Tokens. |
| ES256 | ECDSA P-256 + SHA-256 | Smaller signatures. Modern preference. |
| PS256 | RSA-PSS + SHA-256 | RSA-PSS variant. Marginally more secure than RS256. |
| EdDSA | Ed25519 | Modern, fast, small. Increasingly supported. |
| none | (no signature) | NEVER trust this in production. |
Verifier must check (every time)
- Signature against the issuer's JWKS (key by kid)
- iss exactly matches the expected issuer URL
- aud contains the relying party's identifier
- exp > now (clock-skew tolerance ≤ 5 min)
- nbf ≤ now (when present)
- iat ≤ now (when present)
- alg matches what the issuer is supposed to use (reject alg substitution)
- For OIDC ID Tokens: nonce matches the value sent in /authorize
Common JWT attack vectors
- alg=none — verifier accepts unsigned tokens. Always require a specific alg.
- alg substitution (RS256 → HS256) — attacker signs with public key as HMAC secret.
- kid header injection — attacker controls which key the verifier loads (SQLi / LFI in key resolution).
- jku / x5u header — attacker points the verifier to a key they control. Pin to trusted JWKS only.
- Signature stripping — verifier accepts token with empty signature segment.
- Audience confusion — token meant for service A accepted by service B (mitigated by strict aud check).
JWT vs opaque token
| JWT (self-contained) | No introspection call needed; verifier checks signature locally | Stateless. Hard to revoke before exp. |
| Opaque token | Verifier introspects against issuer (RFC 7662) | Stateful. Trivial to revoke. |
| When to use each | JWT for high-volume API auth; opaque when revocation matters more than throughput |