JWT Explained: What's Inside a JSON Web Token and When to Use One

1 6 114
calendar_todayschedule5 min read

JSON Web Tokens (JWTs) are everywhere in modern authentication — but they're often used without a clear understanding of what they contain and what security guarantees they provide. Here's a practical breakdown.

What is a JWT?

A JWT is a compact, URL-safe string that encodes a JSON payload and optionally signs it. The format is three base64URL-encoded segments separated by dots:

``

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

.

eyJzdWIiOiIxMjM0NTYiLCJuYW1lIjoiQWxpY2UiLCJpYXQiOjE3MTY1Mzc2MDB9

.

SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

`

Part 1 = Header (algorithm and token type)

Part 2 = Payload (claims about the user/session)

Part 3 = Signature (cryptographic proof of authenticity)

You can paste any JWT into a JWT decoder to inspect the contents in plain text — useful for debugging authentication issues.

Decoding the header

The header is a JSON object (base64URL-decoded):

<code>json <p>{</p> <p>"alg": "HS256",</p> <p>"typ": "JWT"</p> <p>}</p> </code>

  • alg: The signing algorithm. Common values: HS256, RS256, ES256.
  • typ: Usually JWT.

Decoding the payload

The payload contains claims — statements about the subject (usually a user) and the token itself:

<code>json <p>{</p> <p>"sub": "1234567890",</p> <p>"name": "Alice",</p> <p>"email": "*Emails are not allowed*",</p> <p>"role": "admin",</p> <p>"iat": 1716537600,</p> <p>"exp": 1716624000</p> <p>}</p> </code>

Standard claims:

| Claim | Meaning |

|-------|---------|

| sub | Subject — the user ID or entity the token is about |

| iss | Issuer — who created the token (e.g., "auth.example.com") |

| aud | Audience — who the token is for |

| exp | Expiration — Unix timestamp after which the token is invalid |

| iat | Issued at — Unix timestamp when the token was created |

| nbf | Not before — Unix timestamp before which the token is invalid |

| jti | JWT ID — unique identifier for this token (useful for revocation) |

You can add any custom claims: roles, permissions, tenant IDs, etc.

The signature

The signature is computed by the token issuer:

For HS256 (HMAC-SHA256):
<code> <p>signature = HMAC-SHA256(base64url(header) + "." + base64url(payload), secret)</p> </code>

For RS256 (RSA-SHA256):
<code> <p>signature = RSA-SHA256(base64url(header) + "." + base64url(payload), private_key)</p> </code>

The receiver verifies the signature using the shared secret (HS256) or public key (RS256). If verification passes, the payload was not tampered with.

Important: Decoding a JWT (base64URL-decoding the payload) is trivial and requires no secret. Verifying a JWT requires the key. Never make access control decisions based on decoded-but-unverified JWT content.

HS256 vs RS256 vs ES256

| Algorithm | How it works | When to use |

|-----------|-------------|-------------|

| HS256 | HMAC with shared secret | Internal services where issuer and verifier are the same system |

| RS256 | RSA with private key (sign) + public key (verify) | When different services verify tokens; public key can be shared |

| ES256 | ECDSA with elliptic curve keys | Same use case as RS256 but with smaller key sizes and faster verification |

The "none" algorithm vulnerability: Some early JWT libraries accepted {"alg": "none"} as a valid algorithm, allowing an attacker to forge tokens by setting the signature to an empty string. Always explicitly reject the "none" algorithm in your validation code.

When to use JWTs

Good use cases:

  • Stateless API authentication: The server doesn't need to look up a session in a database on every request. The JWT itself contains all the information needed to authenticate.
  • Service-to-service authentication: Microservices can issue JWTs to authenticate with each other without a shared session store.
  • Single sign-on (SSO): JWTs are the standard payload format for OIDC (OpenID Connect), the SSO protocol used by Google, GitHub, and enterprise identity providers.

Less ideal use cases:
  • Session tokens in web apps: JWTs cannot be individually revoked before expiry (unless you maintain a blocklist — which reintroduces server state and eliminates the stateless advantage). Traditional opaque session tokens stored server-side are easier to revoke.
  • Large payloads: The JWT travels in every request header. Keep payloads small — store user IDs and roles, not full user profiles.

Verifying JWTs in Node.js

`javascript
const jwt = require('jsonwebtoken');

// Verify and decode

try {

const decoded = jwt.verify(token, secretOrPublicKey);

console.log(decoded.sub, decoded.role);

} catch (err) {

if (err.name === 'TokenExpiredError') {

// Token has expired

} else if (err.name === 'JsonWebTokenError') {

// Invalid signature or malformed token

}

}

`

Never use jwt.decode() (which skips verification) for access control decisions. jwt.verify() validates the signature AND checks expiry automatically.

Verifying JWTs in Python

`python
import jwt # pip install PyJWT

try:

payload = jwt.decode(token, secret_or_public_key, algorithms=["HS256"])

# payload is now a verified dict of claims

except jwt.ExpiredSignatureError:

# Token expired

except jwt.InvalidTokenError:

# Signature verification failed or malformed token

`

Common JWT mistakes

1. Storing JWTs in localStorage

localStorage is accessible to any JavaScript on the page. An XSS attack can steal the JWT. Prefer httpOnly cookies — JavaScript cannot read them.

2. Ignoring expiry

Always set an expiry (exp claim). Short-lived access tokens (15 minutes) with longer-lived refresh tokens (days/weeks) is the standard pattern.

3. Putting sensitive data in the payload

The payload is base64URL-encoded, not encrypted. Anyone with the token can read its contents. Don't put passwords, credit card numbers, or PII in JWT claims.

4. Using "none" algorithm

Explicitly reject the none algorithm in your verification code.

5. Not validating the audience (aud)

If a token was issued for service A, service B should reject it. Set and validate the aud claim.

Debugging JWTs

When a JWT-authenticated request fails, the first step is to decode the token and check:

  1. Has it expired? Check the exp timestamp.
  2. Is the aud (audience) correct for this service?
  3. Does the payload contain the expected claims (role, sub, etc.)?
  4. Is the issuer (iss`) what you expect?

Paste the token into a JWT decoder to inspect all three parts — header, payload, and whether the structure is valid — without running code.


JWTs are a powerful tool for stateless authentication, but they require care. Verify the signature. Set short expiry times. Store tokens securely. Understand what the token contains and what it proves — and what it doesn't.

Originally published at https://snappytools.app/jwt-decoder/

🔥 Join developers growing publicly
Share your knowledge, build in public, and grow your developer presence with a global community.

More Posts

I’m a Senior Dev and I’ve Forgotten How to Think Without a Prompt

Karol Modelskiverified - Mar 19

Comparison: Universal Import vs. Plaid/Yodlee

Pocket Portfolio - Mar 12

5 Web Dev Pitfalls That Are Silently Killing Your Projects (With Real Fixes)

Dharanidharan - Mar 3

How I Built a React Portfolio in 7 Days That Landed ₹1.2L in Freelance Work

Dharanidharan - Feb 9

Beyond Finance: Use Cases for Client-Side ETL

Pocket Portfolio - Mar 19
chevron_left
2.4k Points121 Badges
101Posts
0Comments
SnappyTools builds free, fast, browser-based tools for developers, writers, and designers. No signup... Show more

Related Jobs

View all jobs →

Commenters (This Week)

3 comments
2 comments
1 comment

Contribute meaningful comments to climb the leaderboard and earn badges!