
JWT Tokens Explained: How Authentication Works in Modern Web Apps — and How to Decode Them
What Is a JWT?
A JSON Web Token, or JWT (pronounced "jot"), is an open standard (RFC 7519) for securely transmitting information between parties as a JSON object. Think of it as a digital passport: it contains claims (statements about an entity) that can be verified and trusted because they are digitally signed.
JWTs have become the dominant mechanism for authentication in modern web applications. When you log in to a web app, the server typically issues a JWT, and your browser includes it with every subsequent request. The server verifies the token's signature and extracts your identity from it — no session lookup required.
The key insight is that a JWT is self-contained. Unlike a session ID that is just a random string pointing to server-side data, a JWT carries its own data. This makes JWTs ideal for distributed systems where the service processing a request may not have direct access to the authentication database.
The Three Parts of a JWT
A JWT looks like a long string with two dots separating three sections:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkphbmUgRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
The Header
The header is a JSON object that specifies the token type and signing algorithm:
{ "alg": "HS256", "typ": "JWT" }
This is Base64URL-encoded to produce the first segment. Common algorithms include HS256 (HMAC with SHA-256), RS256 (RSA Signature with SHA-256), and ES256 (ECDSA with SHA-256).
The Payload
The payload contains the claims — the actual data being transmitted. There are three types of claims:
| Claim Type | Examples | Description |
|---|---|---|
| Registered | iss, sub, aud, exp, iat, jti | Predefined by the JWT spec, recommended for interoperability |
| Public | name, email, role | Custom claims defined by you, should be registered in the IANA JWT registry to avoid collisions |
| Private | org_id, permission_level | App-specific claims, use namespaced keys to avoid collisions (e.g., "https://myapp.com/role") |
The Signature
The signature is what makes a JWT trustworthy. For HS256, it is created by taking the Base64URL-encoded header, a dot, the Base64URL-encoded payload, and signing it with a secret key using HMAC SHA-256:
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
For RS256, the server signs with its private key and any party can verify using the public key. This is why RS256 is preferred for microservice architectures — services only need the public key to verify tokens.
JWT vs Session-Based Auth
Understanding when to use JWT versus traditional sessions is crucial. Here is an honest comparison:
| Aspect | JWT | Sessions |
|---|---|---|
| Server state | Stateless | Stateful (server stores session data) |
| Scalability | Easy — any server can verify | Requires shared session store |
| Revocation | Hard — token lives until expiry | Easy — delete the session |
| Token size | Large (1-4KB+) | Small (cookie with session ID) |
| Mobile friendly | Yes — no cookies needed | Cookie management is awkward |
| CSRF | Immune (if in Authorization header) | Vulnerable — needs CSRF tokens |
How JWT Authentication Works
A typical JWT authentication flow looks like this:
- User logs in with credentials (email/password) sent over HTTPS.
- Server verifies credentials against the database.
- Server creates a JWT containing the user's identity (sub claim), roles, and an expiration time, then signs it with its secret or private key.
- Server sends the JWT back to the client in the response body (never in a URL).
- Client stores the token — in memory for access tokens, in an HttpOnly cookie for refresh tokens.
- Client includes the token in the
Authorization: Bearer <token>header of every API request. - Server verifies the signature on each request, checks expiration, and extracts the claims to identify the user.
Critical JWT Vulnerabilities
JWTs are powerful, but they come with real security pitfalls. Here are the most dangerous ones:
1. Algorithm Confusion Attack
If your server accepts multiple algorithms, an attacker can change the alg header from RS256 to HS256. Now the server will try to verify the token using HMAC with the RSA public key as the secret — and if the attacker knows the public key (which is public!), they can forge valid tokens.
alg field from the token header alone.2. "None" Algorithm
Some JWT libraries accept "alg": "none", which means the token has no signature. An attacker can set this header and modify the payload freely. Modern libraries reject this, but older versions may not.
3. Weak Secret Keys
If you use HS256 with a weak secret like "secret" or "password123", an attacker can brute-force it and sign arbitrary tokens. Your HMAC secret should be at least 256 bits (32 bytes) of cryptographically random data.
4. Token Theft via XSS
If you store JWTs in localStorage or accessible cookies, any XSS vulnerability on your site gives the attacker full access to tokens. This is why storing access tokens in memory (not localStorage) and using HttpOnly cookies for refresh tokens is the recommended pattern.
5. Missing Expiration
A JWT without an exp claim never expires. If it is stolen, the attacker has access forever. Always set short expiration times on access tokens.
How to Decode a JWT
Decoding a JWT is straightforward because the header and payload are just Base64URL-encoded JSON. You do not need the secret key to read them — you only need it to verify the signature.
Here is how to decode a JWT manually:
// Split the token into its three parts
const [headerB64, payloadB64, signatureB64] = token.split('.');
// Decode each part
const header = JSON.parse(atob(headerB64.replace(/-/g, '+').replace(/_/g, '/')));
const payload = JSON.parse(atob(payloadB64.replace(/-/g, '+').replace(/_/g, '/')));
console.log('Algorithm:', header.alg);
console.log('Subject:', payload.sub);
console.log('Expires:', new Date(payload.exp * 1000));
Or, skip the manual work and use our JWT Decoder Tool — paste any token and instantly see the decoded header, payload, and signature status.
JWT Best Practices
Use Short-Lived Access Tokens
Set exp to 5-15 minutes. If a token is compromised, the damage window is minimal. Pair with a longer-lived refresh token stored in an HttpOnly cookie.
Prefer RS256 Over HS256
Asymmetric signing means only the auth server knows the private key, while any service can verify with the public key. This eliminates the need to share secrets across services.
Always Validate All Claims
Check exp (not expired), iss (correct issuer), aud (correct audience), and nbf (not before). Skipping any of these is a security vulnerability.
Store Tokens Safely
Access tokens in JavaScript memory (lost on page refresh but safe from XSS). Refresh tokens in HttpOnly, Secure, SameSite cookies. Never in localStorage for sensitive apps.
When Not to Use JWT
JWT is not a silver bullet. There are situations where sessions are genuinely better:
- When you need instant revocation: Logging out should immediately invalidate the token. With JWT, you need a blacklist (which reintroduces server state).
- When your token would be huge: If you need to carry dozens of permissions or large datasets, a JWT can easily exceed several kilobytes, adding overhead to every request.
- When you have a simple monolith: If you are building a single-server app, sessions are simpler, more secure, and have none of JWT's complexity.
- When real-time data is needed: If user permissions change frequently, a JWT cannot reflect those changes until it is reissued. Sessions always reflect the current state.
Try JWT Decoder Tool
Instantly decode and inspect any JWT token. See header, payload, and signature breakdown in real-time.
Frequently Asked Questions
Related Articles

HTTP Headers Explained: The Hidden Metadata That Controls Every Web Page
Dive deep into HTTP headers — the invisible instructions that control caching, security, content types, CORS, and more. Essential knowledge for every web developer.

RFC 4648 Base64 Encoding Explained — How It Works, Examples & Free Tool
Master RFC 4648 Base64 encoding with step-by-step examples. Understand the algorithm, 33% overhead, URL-safe variant, and try our free online Base64 encoder/decoder tool.

Regex Survival Guide: 15 Patterns Every Developer Should Know — and How to Test Them
Master the 5 regex concepts that compose 90% of patterns, then apply them to 15 battle-tested patterns you will use constantly.