Understanding JSON Web Tokens
A JSON Web Token (JWT), pronounced "jot," is a compact, URL-safe token defined by RFC 7519 that carries a set of signed claims — assertions about a user or system — as three Base64URL-encoded parts joined by dots: Header.Payload.Signature. The header names the signing algorithm, the payload holds the claims (who the user is, when the token expires, what they can do), and the signature is a cryptographic hash over the first two parts that lets any recipient prove the token has not been altered. Because everything needed to trust the token travels inside the token itself, a server can verify a request without looking anything up in a session store — that is what makes JWTs "stateless." A critical caveat: a standard JWT is signed, not encrypted, so anyone holding it can read the payload; the signature guarantees integrity, not secrecy.
That is the definition an AI Overview will give you. What it can't show you is the shape of how a token moves through a login, why a one-character change breaks verification, or how to check whether your own implementation is safe. Below you'll find an animated diagram of the full authentication flow, a decoded real token you can inspect live, a JWT-vs-session decision table, and a validation checklist you can run against your own code.
The Structure of a JWT
A JWT consists of three Base64URL-encoded sections separated by dots (periods), creating a compact string that looks like this:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
This seemingly random string actually contains three distinct components: Header.Payload.Signature. The two dots split the token into the three color-coded segments shown below — the first two are just encoded JSON you can read, and the third is the signature that binds them together.
Want to see the decoded contents of a real token right now? Paste one into the decoder below — it runs entirely in your browser, so nothing is sent to a server:
1. Header
The header typically consists of two parts: the token type (always "JWT") and the signing algorithm being used (e.g., HMAC SHA256, RSA, ECDSA).
Example Header:
{
"alg": "HS256",
"typ": "JWT"
}
Common Algorithm Values:
HS256- HMAC with SHA-256 (symmetric)RS256- RSA Signature with SHA-256 (asymmetric)ES256- ECDSA with SHA-256 (asymmetric)PS256- RSA PSS with SHA-256 (asymmetric)EdDSA- Edwards-curve Digital Signature Algorithm (asymmetric)
This JSON is then Base64URL encoded to form the first part of the JWT.
2. Payload
The payload contains the claims—statements about an entity (typically the user) and additional data. Claims are categorized into three types:
Registered Claims: Predefined claims recommended by the JWT specification:
iss(issuer) - Who issued the tokensub(subject) - Who the token is about (usually user ID)aud(audience) - Who the token is intended forexp(expiration time) - When the token expires (Unix timestamp)nbf(not before) - When the token becomes validiat(issued at) - When the token was createdjti(JWT ID) - Unique identifier for the token
Public Claims: Standardized claims defined in the IANA JSON Web Token Registry or custom claims using collision-resistant names (typically namespaced URIs).
Private Claims: Custom claims created by agreement between parties sharing tokens, used to transmit application-specific information.
Example Payload:
{
"sub": "1234567890",
"name": "John Doe",
"email": "john.doe@example.com",
"roles": ["user", "admin"],
"iat": 1516239022,
"exp": 1516242622
}
The payload is Base64URL encoded to form the second part of the JWT.
Important Security Note: The payload is encoded, not encrypted. Anyone who possesses the token can decode and read its contents. Never store sensitive information like passwords, credit card numbers, or social security numbers in a JWT payload.
3. Signature
The signature ensures that the token hasn't been tampered with. It's created by taking the encoded header, encoded payload, a secret (for symmetric algorithms) or private key (for asymmetric algorithms), and applying the algorithm specified in the header.
For HS256 (HMAC):
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)
For RS256 (RSA):
RSASHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
privateKey
)
The signature forms the third part of the JWT. Recipients can verify the signature using the secret (HS256) or public key (RS256) to ensure the token is authentic and hasn't been modified.
How JWT Authentication Works
The typical JWT authentication flow follows this pattern. The animated diagram below traces a single token from login to a protected request — watch the token travel from the server to the client and back on every call, with the server verifying the signature instead of looking up a session:
1. User Login
The user provides credentials (username and password) to the authentication server:
POST /api/auth/login
{
"username": "john@example.com",
"password": "securePassword123"
}
2. Token Generation
The server validates the credentials and generates a JWT containing the user's identity and permissions:
const jwt = require('jsonwebtoken');
const payload = {
sub: user.id,
email: user.email,
roles: user.roles,
iat: Math.floor(Date.now() / 1000),
exp: Math.floor(Date.now() / 1000) + (60 * 15) // 15 minutes
};
const token = jwt.sign(payload, process.env.JWT_SECRET);
The server returns the token to the client:
{
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"tokenType": "Bearer",
"expiresIn": 900
}
3. Client Storage
The client stores the token securely (ideally in HttpOnly cookies for web applications, or secure storage for mobile apps).
4. Authenticated Requests
For subsequent requests to protected resources, the client includes the JWT in the Authorization header:
GET /api/user/profile
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
5. Token Verification
The server extracts the token from the Authorization header, verifies the signature, checks expiration, and extracts user information:
const jwt = require('jsonwebtoken');
function authenticateToken(req, res, next) {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) {
return res.sendStatus(401); // Unauthorized
}
jwt.verify(token, process.env.JWT_SECRET, (err, payload) => {
if (err) {
return res.sendStatus(403); // Forbidden (invalid token)
}
req.user = payload; // Attach user info to request
next();
});
}
6. Access Granted
If verification succeeds, the server processes the request using the user information from the JWT payload.
JWT vs Traditional Session-Based Authentication
Understanding the differences between JWT and traditional sessions helps clarify when to use each approach. Here is the head-to-head, ending with a plain recommendation for which to reach for:
| Dimension | JWT (stateless token) | Server session (opaque ID) |
|---|---|---|
| Where state lives | Inside the token, on the client | In server memory / Redis / DB |
| Server storage needed | None | Yes (shared store to scale) |
| Revocation | Hard — valid until exp unless you add a blocklist | Instant — delete the session |
| Horizontal scaling | Trivial — any node can verify | Needs shared session store |
| What the client holds | Readable claims (signed, not secret) | Random ID that reveals nothing |
| Cross-domain / mobile / SSO | Natural fit | Awkward (cookie same-origin limits) |
| Main risk | Stolen token works until expiry | Session store is a scaling bottleneck |
| Which should I use? | APIs, microservices, SSO, mobile, serverless — anywhere statelessness and scale matter and short expiry is acceptable | Server-rendered apps and high-security flows (banking, healthcare) that need instant logout and easy revocation |
Traditional Sessions
How It Works:
- User logs in
- Server creates session ID and stores user data in server memory or database
- Session ID sent to client in cookie
- Client sends session ID cookie with each request
- Server looks up session data using the ID
Characteristics:
- Stateful: Server maintains session storage
- Server-side storage required: Redis, database, or in-memory store
- Easy revocation: Delete session to immediately logout user
- Scalability challenges: Horizontal scaling requires shared session storage
- Lower security risk: Session ID reveals no user information
JWT Authentication
How It Works:
- User logs in
- Server generates JWT with user data and signs it
- JWT sent to client
- Client sends JWT with each request
- Server verifies signature and extracts user data
Characteristics:
- Stateless: Server doesn't store sessions
- No server-side storage: All data in the token
- Difficult revocation: Tokens valid until expiration (workarounds exist)
- Highly scalable: No shared state between servers
- Higher security risk: Token contains user information (though signed)
When to Use JWT
JWT excels in specific scenarios:
Ideal Use Cases
Microservices Authentication: Multiple services can verify tokens using the same secret or public key without coordinating session storage.
Single Sign-On (SSO): Users authenticate once and use the same token across multiple applications or domains.
Mobile Applications: Native apps store tokens locally and include them with API requests without managing cookies.
Stateless APIs: RESTful APIs benefit from statelessness; JWTs eliminate server-side session management.
Cross-Domain Authentication: JWTs work seamlessly across different domains, unlike cookie-based sessions restricted by same-origin policy.
Authorization: JWTs can include user permissions and roles, enabling fine-grained access control without database lookups.
When to Avoid JWT
Long-Lived Sessions: For sessions lasting weeks or months, the inability to easily revoke JWTs becomes problematic.
Sensitive Applications: Banking or healthcare applications requiring instant logout capability should use session tokens for immediate revocation.
High-Security Requirements: When security outweighs scalability, session-based authentication with server-side revocation is safer.
Server-Rendered Applications: Traditional web apps rendering HTML on the server may find session cookies simpler and more appropriate.
JWT Security Best Practices (2025)
Implementing JWT securely requires following established best practices:
1. Use Strong Algorithms
- Prefer RS256, ES256, or EdDSA over HS256 for production systems
- Never use "none" algorithm: Always verify the algorithm explicitly
- Reject algorithm switching: Don't trust the
algclaim in the header blindly
2. Keep Tokens Short-Lived
- Access tokens: 5-15 minutes lifespan
- Refresh tokens: 7-14 days maximum
- Short expiration limits damage from token theft
3. Implement Refresh Token Rotation
- Issue new refresh token with each refresh request
- Invalidate old refresh token immediately
- Detect token reuse attacks (old refresh token used again)
4. Secure Token Storage
- Web apps: HttpOnly, Secure, SameSite cookies
- Mobile apps: Secure device storage (Keychain, Keystore)
- Never localStorage: Vulnerable to XSS attacks
5. Validate Everything
- Verify signature before trusting any claim
- Check expiration (
exp) and not-before (nbf) times - Validate issuer (
iss) and audience (aud) claims - Reject tokens with unexpected algorithms
6. Use HTTPS Exclusively
- Transmit tokens only over encrypted connections
- Prevents man-in-the-middle interception
- Essential for security regardless of token type
7. Minimize Token Payload
- Include only essential information in payload
- Reduce token size for performance
- Avoid sensitive data in claims
8. Implement Token Revocation
Despite JWT's stateless nature, critical applications need revocation:
- Maintain server-side blocklist of revoked tokens
- Use short expiration times to limit blocklist size
- Consider token versioning (invalidate all tokens before version X)
- Monitor for anomalous token usage patterns
JWT Verification Checklist
Most real-world JWT vulnerabilities come from skipping a verification step, not from a broken algorithm. Run your token-handling code against this list — every item is a place attackers actually probe:
- Verify the signature before reading any claim. Decoding is not verifying. Never trust
sub,roles, or anything else until the signature checks out. - Pin the accepted algorithm(s) server-side. Reject
"none", and reject a token whosealgdoesn't match what you expect — don't let the token tell you how to verify it (the RS256→HS256 confusion attack). - Check
exp(andnbfif used). An expired or not-yet-valid token must be rejected, allowing only a small clock-skew tolerance (a few seconds to a couple minutes). - Validate
issandaud. Confirm the token was issued by your authority and intended for your service, so a valid token from another system can't be replayed at yours. - Keep access tokens short (5–15 min) and use refresh-token rotation for longer sessions.
- Store tokens in HttpOnly + Secure + SameSite cookies (or the mobile secure store) — never localStorage.
- Transmit only over HTTPS and keep no secrets in the payload.
- Have a revocation path — a
jtiblocklist or token versioning — for logout and compromise response.
Common JWT Use Cases
Real-world applications leverage JWTs in diverse scenarios:
API Authentication: Third-party developers integrate with your API using JWTs for authentication, including rate limiting and permission claims in the token.
OAuth 2.0 and OpenID Connect: JWT is the standard token format for OAuth 2.0 access tokens and OpenID Connect ID tokens.
Serverless Functions: AWS Lambda, Azure Functions, and Google Cloud Functions use JWTs to authenticate requests without maintaining session state.
WebSockets: Authenticate WebSocket connections by passing JWT during handshake.
Mobile App Backend: Mobile apps authenticate API requests using JWTs stored in secure device storage.
Multi-Tenant SaaS: JWTs include tenant ID, enabling applications to serve multiple customers with proper isolation.
Conclusion
JSON Web Tokens have revolutionized modern authentication by providing a compact, self-contained, and stateless mechanism for securely transmitting identity and authorization information. Their structure—header, payload, and signature—balances human readability with cryptographic security.
While JWTs offer significant advantages for scalability, cross-domain authentication, and stateless architectures, they require careful implementation to avoid common security pitfalls. Following best practices around algorithm selection, token lifespan, secure storage, and validation ensures robust authentication systems.
Understanding JWTs is essential for modern developers building APIs, microservices, mobile applications, or any system requiring secure, scalable authentication. Whether you're implementing SSO, securing a REST API, or building serverless functions, JWTs provide the foundation for trustworthy identity management.
Ready to inspect and understand JWTs? Use our free JWT Decoder tool to decode tokens, examine headers and payloads, analyze algorithms, and check expiration times—all processed entirely in your browser for maximum privacy and security.