jwt decode security authentication tokens

Understanding JWTs: How to Decode and Generate JSON Web Tokens

A comprehensive guide to JSON Web Tokens (JWT). Learn how to decode, verify, and generate tokens for secure authentication debugging.

2026-04-09 Use This Tool

Introduction: What Is a JSON Web Token?

JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact, self-contained way to securely transmit information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (HMAC algorithm) or a public/private key pair (RSA or ECDSA).

JWTs were created to address a common challenge in distributed systems: how can a server trust a request from a client without needing to look up a session in a database? The answer is to encode and sign the user identity and permissions directly into a token that the client carries with every request.

Today, JWTs are the backbone of modern authentication and authorization:

  • Single Sign-On (SSO): One login grants access to multiple services.
  • API Authentication: Mobile apps and SPAs authenticate API calls with a Bearer token.
  • OAuth 2.0 / OpenID Connect: JWTs serve as access tokens and ID tokens.
  • Microservices: Services verify caller identity without a central session store.

JWT Structure: header.payload.signature

A JWT is a string of three Base64url-encoded parts separated by dots (.):

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Part 1 — Header decodes to:

{
  "alg": "HS256",
  "typ": "JWT"
}

The header specifies the signing algorithm (alg) and token type (typ).

Part 2 — Payload decodes to:

{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022
}

The payload contains the claims — statements about the entity (typically a user) and metadata.

Part 3 — Signature (for HS256) is computed as:

HMACSHA256(
  base64UrlEncode(header) + "." + base64UrlEncode(payload),
  secret
)

The signature ensures the token has not been tampered with. Critical: The payload is only Base64url-encoded, not encrypted. Anyone who obtains the token can read all claims.

Signing Algorithms: HS256, RS256, and ES256

HS256 (HMAC-SHA256) — Symmetric

Uses a single shared secret for both signing and verification. Simple to implement, but the same secret must be known by both issuer and verifier.

  • Best for: Monolithic applications or tightly coupled services sharing a secret.
  • Risk: If the secret leaks, all tokens can be forged.

RS256 (RSA-SHA256) — Asymmetric

Uses a private key to sign and a public key to verify. The private key stays secret; the public key can be shared freely via a JWKS endpoint.

  • Best for: Distributed systems, microservices, and multi-service verification.
  • Key size: Minimum 2048-bit RSA key recommended.

ES256 (ECDSA-SHA256) — Asymmetric

Uses the Elliptic Curve Digital Signature Algorithm with the P-256 curve. Produces smaller signatures than RS256 with equivalent security.

  • Best for: Performance-sensitive or bandwidth-constrained environments.
Algorithm Type Key Signature Size Use Case
HS256 Symmetric Shared secret 32 bytes Single-service apps
RS256 Asymmetric RSA key pair 256+ bytes Distributed systems
ES256 Asymmetric EC key pair 64 bytes High-performance APIs

JWT Claims: Registered, Public, and Private

Claims are statements about an entity (typically a user) and metadata encoded in the payload.

Registered Claims (IANA-defined)

Claim Full Name Description
iss Issuer Who issued the token (e.g., "auth.example.com")
sub Subject Whom the token refers to (e.g., a user ID)
aud Audience Who the token is intended for
exp Expiration Unix timestamp after which the token is invalid
nbf Not Before Unix timestamp before which the token must not be used
iat Issued At Unix timestamp when the token was issued
jti JWT ID Unique identifier; prevents replay attacks

Public Claims

Defined in the IANA JWT Claims Registry. Examples: email, name, picture, roles.

Private Claims

Custom claims agreed upon by issuer and consumer. Use namespaced names to avoid collisions:

{
  "https://example.com/tenant_id": "acme-corp",
  "https://example.com/roles": ["admin", "editor"]
}

How JWT Authentication Flow Works

  1. User Login: The client sends credentials to the authentication server.
  2. Token Issuance: The server validates credentials, creates a signed JWT with appropriate claims, and returns it.
  3. Token Storage: The client stores the JWT (in memory, localStorage, or an HttpOnly cookie).
  4. Authenticated Requests: For each subsequent request to a protected resource, the client includes the JWT:
    Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
    
  5. Token Verification: The server verifies the signature, checks exp, and grants or denies access.
  6. Token Refresh: When the access token expires, the client uses a long-lived refresh token to obtain a new one.
Client            Auth Server         Resource Server
  |                    |                     |
  |-- POST /login ---->|                     |
  |<-- 200 + JWT ------|                     |
  |                    |                     |
  |-- GET /api (Authorization: Bearer JWT) ->|
  |                    |      verify JWT     |
  |<----------- 200 + data ----------------->|

Security Considerations

The Payload Is NOT Encrypted

Base64url encoding is not encryption. Anyone who holds a JWT can decode and read all claims instantly. Never include in a JWT payload:

  • Passwords or password hashes
  • Credit card or financial data
  • Sensitive PII beyond what is necessary
  • Private keys or internal secrets

Always Verify the Signature

// Node.js — jsonwebtoken library
const jwt = require("jsonwebtoken");

try {
  const decoded = jwt.verify(token, process.env.JWT_SECRET);
  console.log(decoded.sub); // Trusted user ID
} catch (err) {
  res.status(401).json({ error: "Invalid or expired token" });
}

Use Short Expiration Times

Access tokens should have short lifespans (15 minutes to 1 hour). Use refresh tokens for long-lived sessions to limit damage from token theft.

Always Use HTTPS

Never transmit tokens over unencrypted connections. An intercepted token is as good as a stolen password.

Common Vulnerabilities

1. Algorithm Confusion Attack (CVE-2015-9235)

An attacker changes alg in the header from RS256 to HS256, then signs the token using the server's public key as the HMAC secret. A naive server accepts it.

Mitigation: Always specify the expected algorithm explicitly:

jwt.verify(token, publicKey, { algorithms: ["RS256"] });

2. The "none" Algorithm Attack

If a server accepts alg: "none", an attacker can craft tokens with no signature at all.

Mitigation: Explicitly reject tokens with alg: "none". Modern libraries have patched this, but older versions remain vulnerable.

3. Weak Secrets (HS256)

A short or guessable HMAC secret can be brute-forced offline using tools like hashcat against a captured token.

Mitigation: Use a cryptographically random secret of at least 256 bits:

openssl rand -hex 32

4. Missing Claim Validation

Failing to validate iss, aud, or nbf allows cross-service token reuse or premature token use.

5. Insecure Token Storage

localStorage is accessible to any JavaScript on the page, making tokens vulnerable to XSS. Prefer HttpOnly cookies.

JWT vs Session Tokens: Stateless vs Stateful

Feature JWT (Stateless) Session Token (Stateful)
Storage Client-side Server-side (DB or cache)
Scalability Excellent — no server lookup required Requires a shared session store
Revocation Difficult — valid until exp Easy — delete session from store
Token size Larger per request Small opaque token, data on server
Microservices Ideal — verify independently Requires shared session infrastructure
Payload visibility Readable by anyone with the token Data stays server-side

Choose JWT when building distributed systems, stateless REST APIs, or mobile apps. Choose sessions when you need instant revocation (e.g., "log out everywhere").

The Revocation Challenge

Because JWTs are self-contained, you cannot cancel a token before it expires without extra infrastructure:

  • Short expiry + refresh tokens: Access tokens expire fast; revoke refresh tokens in the DB.
  • Token blocklist: Store revoked jti values in Redis (reintroduces some statefulness).
  • Secret rotation: Rotate the signing secret to invalidate all tokens (disrupts all users).

OAuth 2.0 and OpenID Connect

OAuth 2.0

OAuth 2.0 is an authorization framework. JWTs are widely used as access tokens because they are self-contained and carry authorization scopes:

{
  "iss": "https://auth.example.com",
  "sub": "user_123",
  "aud": "https://api.example.com",
  "scope": "read:profile write:posts",
  "exp": 1893456000
}

OpenID Connect (OIDC)

OpenID Connect is an identity layer on top of OAuth 2.0. It introduces the ID Token, always a JWT, containing identity claims about the authenticated user:

{
  "iss": "https://accounts.google.com",
  "sub": "110169484474386276334",
  "aud": "my-client-id.apps.googleusercontent.com",
  "email": "[email protected]",
  "name": "Jane Doe",
  "exp": 1893456000,
  "iat": 1893452400
}

OIDC also defines the JWKS endpoint (/.well-known/jwks.json), where the authorization server publishes its public keys. Any service can fetch these keys to verify tokens without a prior key exchange.

Best Practices

  1. Use asymmetric algorithms (RS256/ES256) for public-facing systems — protect the private signing key while letting anyone verify with the public key.
  2. Set short exp values for access tokens — 15 minutes is a common safe default.
  3. Always rotate refresh tokens — issue a new one each time it is used; invalidate the old one.
  4. Validate all relevant claims — check iss, aud, exp, and nbf at minimum.
  5. Specify the expected algorithm explicitly — never rely solely on the alg field in the token header.
  6. Never put sensitive data in the payload — it is encoded, not encrypted.
  7. Always use HTTPS — never transmit tokens over unencrypted connections.
  8. Store tokens securely — prefer HttpOnly, Secure, SameSite=Strict cookies over localStorage.
  9. Implement a token revocation strategy — use a blocklist or short-lived tokens with refresh rotation.
  10. Keep JWT libraries up to date — security vulnerabilities are patched in newer releases.

FAQ

Q: Can I decrypt a JWT payload? A: JWT payloads are Base64url-encoded, not encrypted — no key is needed to read them. If you need confidentiality, use JWE (JSON Web Encryption, RFC 7516), which encrypts the payload.

Q: What is the difference between decode and verify? A: decode simply Base64url-decodes the parts and returns JSON with no security checks. verify also validates the signature against your key and checks time-based claims like exp. Always use verify in production.

Q: How do I handle token refresh securely? A: Issue a short-lived access token (15–60 min) and a long-lived refresh token (days to weeks). Store refresh tokens server-side so they can be revoked. When the access token expires, the client sends the refresh token to a dedicated endpoint to get a new access token.

Q: Can I use JWT for both authentication and authorization? A: Yes. Include identity claims (who the user is) and authorization claims (what they can do, e.g., roles, scope) in the same token. Keep the payload size reasonable.

Q: What happens if my JWT secret leaks? A: An attacker can forge valid tokens for any user. Rotate the secret immediately (this invalidates all existing tokens), investigate the breach, and force all users to re-authenticate.

Q: Should I use HS256 or RS256? A: Use RS256 (or ES256) when multiple services verify tokens or when the verifier is not fully trusted. Use HS256 only when the same service both issues and verifies tokens within a tightly controlled boundary.

Q: How large can a JWT get? A: Keep JWTs under 4 KB to avoid HTTP header size limits and performance issues. Do not embed large data structures; reference them by ID instead.