JWT Explained: How JSON Web Tokens Work

A complete guide to JSON Web Tokens — from the structure and signing algorithms to security best practices and real-world implementation.

Developer Tools 2026-04-09 By Risetop Team 12 min read

If you've worked on any modern web application, you've almost certainly encountered JWTs. Those long strings with three sections separated by dots — eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.abc123def — are everywhere: in your browser's cookies, in API request headers, and in mobile app authentication flows.

But what exactly is a JWT? How does it work under the hood? And what are the security implications of using it? This guide answers all of these questions with clear explanations and practical examples.

What Is a JWT?

JWT (JSON Web Token) is an open standard (RFC 7519) for securely transmitting information between parties as a JSON object. It's compact, URL-safe, and self-contained — meaning it carries all the necessary information about the user within itself.

JWTs are most commonly used for:

The Structure of a JWT

A JWT consists of three parts, separated by dots (.):

xxxxx.yyyyy.zzzzz Header . Payload . Signature

1. Header

The header identifies the token type and the signing algorithm:

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

This JSON is base64url-encoded to form the first part of the JWT.

2. Payload

The payload contains the claims — statements about the entity (usually the user) and additional metadata:

{ "sub": "1234567890", "name": "John Doe", "email": "john@example.com", "role": "admin", "iat": 1712678400, "exp": 1712764800 }

There are three types of claims:

⚠️ Critical: The payload is NOT encrypted — it's only base64-encoded. Anyone who intercepts the token can decode and read the payload. Never put passwords, API keys, credit card numbers, or any sensitive data in a JWT payload. The signature only ensures the token hasn't been modified, not that the contents are secret.

3. Signature

The signature is created by combining the encoded header, encoded payload, a secret key, and the algorithm specified in the header:

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

The signature ensures the token hasn't been tampered with. If anyone modifies the header or payload, the signature won't match, and the server will reject the token.

How JWT Authentication Works

Here's the typical flow of JWT-based authentication:

  1. User logs in with their credentials (username/password).
  2. Server verifies credentials and creates a JWT containing the user's ID, role, and an expiration time.
  3. Server sends the JWT back to the client.
  4. Client stores the JWT (in a cookie, localStorage, or memory) and includes it in the Authorization header of subsequent requests.
  5. Server verifies the JWT signature on each request and extracts the user information.
  6. When the token expires, the client must obtain a new one (either by re-authenticating or using a refresh token).
// Client sending a JWT with every request fetch('/api/profile', { headers: { 'Authorization': 'Bearer eyJhbGciOiJIUzI1NiJ9...' } });

JWT Signing Algorithms

The algorithm specified in the header determines how the signature is created. Understanding the differences is critical for security:

Symmetric Algorithms (HMAC)

With HMAC, the same secret key is used to both create and verify the signature. This means both the authentication server and the resource server must share the same secret.

Asymmetric Algorithms (RSA / ECDSA)

Asymmetric algorithms are ideal when you have a separate authentication service: the auth service signs with a private key, and any service can verify with the public key. This is how OAuth providers like Google and Auth0 work.

💡 Recommendation: Use RS256 or ES256 for microservice architectures where multiple services need to verify tokens. Use HS256 for simpler single-service applications where the same server both issues and verifies tokens.

Decoding a JWT

Since the header and payload are just base64url-encoded, you can decode them without any key:

// Decoding in JavaScript (no library needed) const token = 'eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.abc'; // Split into parts const [headerB64, payloadB64, signature] = token.split('.'); // Decode header const header = JSON.parse(atob(headerB64)); // { "alg": "HS256", "typ": "JWT" } // Decode payload const payload = JSON.parse(atob(payloadB64)); // { "sub": "1234567890" } // Note: atob() works with standard base64. // For base64url, you may need to replace - with + and _ with /

This is why you should never store sensitive information in a JWT — anyone with the token can read the payload.

Want to inspect a JWT right now? Try our free JWT Decoder tool — paste any token and instantly see its header, payload, and signature details.

JWT vs Session-Based Authentication

Understanding when to use JWT vs traditional sessions helps you make the right architectural decision:

AspectSessionsJWT
Server StorageRequired (memory/Redis/DB)Not required (stateless)
ScalabilityRequires shared session storeNaturally scalable
Mobile FriendlyCookie management is complexEasy — just send in header
RevocationEasy — delete from storeHard — token valid until expiry
SizeSmall (session ID)Larger (full payload)
Cross-DomainComplex (CORS + cookies)Simpler (Authorization header)

JWT Security Best Practices

1. Use Strong Signing Algorithms

Always specify the algorithm explicitly when verifying tokens. The "alg: none" attack exploits servers that accept unsigned tokens. Never use the none algorithm in production.

// Bad — allows algorithm spoofing jwt.verify(token, secret); // Uses algorithm from header // Good — force the expected algorithm jwt.verify(token, secret, { algorithms: ['HS256'] });

2. Keep Expiration Short

Set short expiration times (15–30 minutes for access tokens) and use refresh tokens for longer sessions:

const token = jwt.sign( { sub: userId, role: 'admin' }, secret, { expiresIn: '15m' } // Short-lived access token );

3. Store Tokens Securely

4. Validate All Claims

Don't just verify the signature — also check the claims:

jwt.verify(token, secret, { algorithms: ['RS256'], issuer: 'https://auth.yoursite.com', audience: 'https://api.yoursite.com', clockTolerance: 30 // Allow 30s clock skew });

5. Use HTTPS Always

JWTs sent over HTTP can be intercepted by anyone on the network. Always serve your application over HTTPS to protect tokens in transit.

6. Don't Put Sensitive Data in the Payload

Since anyone can decode the payload with base64, treat it like plaintext. Never include passwords, Social Security numbers, API secrets, or financial data.

Common JWT Vulnerabilities

Algorithm Confusion Attack

An attacker changes the alg header from RS256 to HS256. If the server uses the public RSA key as the HMAC secret (which some implementations do), the attacker can forge valid tokens. Always explicitly specify the expected algorithm.

Token Injection via jku (JWK Set URL)

The jku (JWK Set URL) header claim points to a URL containing the public key. An attacker can set this to their own server and provide their own key. Always whitelist allowed JWK URLs or don't support the jku header.

Cross-Site Scripting (XSS) Token Theft

If a JWT is stored in localStorage, any XSS vulnerability on your site can steal it. This is why HttpOnly cookies are the recommended storage method.

Implementing JWT in Node.js

const jwt = require('jsonwebtoken'); const SECRET = 'your-256-bit-secret'; // Create a token function createToken(user) { return jwt.sign( { sub: user.id, email: user.email, role: user.role }, SECRET, { expiresIn: '15m', issuer: 'yourapp' } ); } // Verify a token function verifyToken(token) { try { return jwt.verify(token, SECRET, { algorithms: ['HS256'], issuer: 'yourapp' }); } catch (err) { return null; // Invalid or expired } } // Express middleware function authMiddleware(req, res, next) { const auth = req.headers.authorization; if (!auth || !auth.startsWith('Bearer ')) { return res.status(401).json({ error: 'No token provided' }); } const decoded = verifyToken(auth.slice(7)); if (!decoded) { return res.status(401).json({ error: 'Invalid token' }); } req.user = decoded; next(); }

When to Use (and Not Use) JWT

Good use cases for JWT:

Consider alternatives when:

Summary

JWT is a powerful tool for stateless authentication, but it requires careful implementation to be secure:

JWTs aren't a silver bullet, but understanding how they work — and their limitations — lets you use them effectively and securely in your applications.