What is JWT (JSON Web Token)? Stateless Authentication Explained
JWT is an open standard for securely transmitting claims between parties as a compact, signed JSON object — the foundation of stateless authentication in APIs, microservices, and single sign-on systems.
TL;DR JWT (JSON Web Token) is an open standard (RFC 7519) that encodes claims — assertions about a user or entity — into a compact, URL-safe string that can be cryptographically signed and verified. A JWT has three parts: header, payload, and signature. When a user authenticates, the server issues a JWT the client sends with every subsequent request. The server verifies the signature without querying a database, enabling stateless authentication. JWT access tokens should expire in 5–60 minutes. Refresh tokens handle long-lived sessions.
What is JWT?
JWT (JSON Web Token) is an open standard (RFC 7519) for securely transmitting information between parties as a compact, URL-safe JSON object. A JWT encodes claims — statements about an entity (typically a user) — and is cryptographically signed so the recipient can verify its authenticity and integrity without contacting the issuer.
JWT solves a specific problem: how to pass verified identity information between services in a stateless system without storing session data on the server.
How JWT works
- User authenticates — Client sends credentials (username + password) to the auth server.
- Server issues JWT — Auth server validates credentials, creates a JWT with user claims (user ID, roles, expiration), signs it with a secret or private key, and returns it.
- Client stores and sends JWT — Client stores the JWT (typically in an HttpOnly cookie or memory) and sends it in the
Authorization: Bearer <token>header with every request. - Server verifies JWT — Server checks the signature, validates expiration and claims, and processes the request — no database lookup required.
This flow is stateless: the server holds no session data. Any server instance that knows the signing key can verify the token.
JWT structure: three parts
A JWT is three Base64URL-encoded segments separated by dots: header.payload.signature
Header
{ "alg": "HS256", "typ": "JWT"}Specifies the signing algorithm (HS256, RS256, ES256) and token type. Base64URL-encoded.
Payload
{ "sub": "user_123", "name": "Maria Silva", "role": "admin", "iat": 1712345678, "exp": 1712349278}Contains claims. Registered claims (defined by RFC 7519):
| Claim | Name | Description |
|---|---|---|
sub | Subject | Unique identifier of the entity (user ID) |
iss | Issuer | Service that issued the token |
aud | Audience | Intended recipient(s) of the token |
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 |
JWT payloads are Base64URL-encoded, not encrypted. Anyone who intercepts a JWT can decode and read the payload. Never store passwords, credit card numbers, or PII in JWT claims.
Signature
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)The signature ensures the token hasn’t been tampered with. The server verifies it using the same secret (HMAC) or the issuer’s public key (RSA/ECDSA).
Signing algorithms: HMAC vs RSA
| Algorithm | Type | Key | Use when |
|---|---|---|---|
HS256 | Symmetric (HMAC) | Shared secret | Single service; both issuer and verifier share the secret |
RS256 | Asymmetric (RSA) | Public/private key pair | Multiple services; issuer signs with private key, any service verifies with public key |
ES256 | Asymmetric (ECDSA) | Public/private key pair | Same as RS256, with smaller key sizes and faster performance |
Use HS256 for simple, single-service authentication. Use RS256 or ES256 when multiple independent services need to verify tokens without access to the signing secret.
Access tokens vs refresh tokens
| Access token | Refresh token | |
|---|---|---|
| Purpose | Authorizes API access | Obtains new access tokens |
| Lifetime | Short: 5–60 minutes | Long: hours to weeks |
| Sent with | Every API request | Only token refresh endpoint |
| Storage | Memory or HttpOnly cookie | Secure HttpOnly cookie |
| Revocable | Not without infrastructure | Yes, via token database |
Short-lived access tokens limit the damage from token theft. Refresh tokens enable long sessions without requiring the user to re-authenticate.
JWT vs server-side sessions
| JWT | Server-side session | |
|---|---|---|
| State | Stateless — no server storage | Stateful — session stored on server |
| Scalability | Any server verifies any token | Requires sticky sessions or shared session store |
| Revocation | Cannot revoke before expiration without a blacklist | Immediate revocation by deleting session |
| Size | Larger (token in every request) | Small (only session ID in cookie) |
| Best for | APIs, microservices, SSO, mobile apps | Web apps requiring immediate logout |
Common JWT security mistakes
| Mistake | Risk | Fix |
|---|---|---|
| Storing PII in payload | Anyone can decode the payload | Store only non-sensitive claims (user ID, roles) |
| Long-lived access tokens (hours/days) | Token theft has long exposure window | Set exp to 5–60 minutes |
Not validating the alg claim | Algorithm confusion attacks (e.g., RS256 → HS256 swap) | Always enforce expected algorithm server-side |
Storing JWT in localStorage | XSS attacks can steal the token | Use HttpOnly, Secure, SameSite=Strict cookies |
| No token revocation plan | Compromised tokens remain valid until expiry | Implement short expiration + refresh token rotation |
Not validating iss and aud | Tokens from other services accepted | Always validate issuer and audience claims |
When to use JWT
Use JWT when you need:
- Stateless authentication for REST or GraphQL APIs
- Single Sign-On (SSO) across multiple applications or domains
- Authorization claims embedded in the token (roles, scopes, permissions)
- Mobile apps or SPAs that cannot maintain server-side sessions
- Service-to-service authentication in microservices
Do not use JWT when you need:
- Immediate session revocation (e.g., logout should instantly block all requests)
- Server-side logging of every user action with session context
- Tokens exceeding ~8KB (bandwidth overhead grows with every request)
Frequently asked questions
What is JWT in simple terms? JWT is a token that proves who you are and what you’re allowed to do. The server creates it, signs it cryptographically, and gives it to you. You send it with every request. The server verifies the signature without looking you up in a database.
Is JWT safe? JWT is safe when used correctly. The signature prevents tampering. However, the payload is only Base64-encoded — not encrypted — so anyone who obtains the token can read its contents. Use HTTPS for all token transmission and never put sensitive data in the payload.
What is the difference between JWT and OAuth? OAuth 2.0 is an authorization framework that defines how tokens are issued and used. JWT is a token format. OAuth 2.0 commonly uses JWT as its access token format, but JWT can be used independently of OAuth.
How do I invalidate a JWT before it expires? JWTs cannot be invalidated without additional infrastructure. Options: (1) use short expiration times (5–15 minutes), (2) maintain a token blacklist in a cache (Redis), (3) use a version claim in the payload that becomes invalid when the user changes their password or logs out.
What is the difference between HS256 and RS256? HS256 uses a single shared secret — both the issuer and any verifier must know it. RS256 uses a public/private key pair — the issuer signs with the private key, and any verifier can confirm with the public key without access to the secret. Use RS256 when multiple independent services need to verify tokens.
Should I store JWT in localStorage or cookies? Store JWT in HttpOnly, Secure, SameSite=Strict cookies. localStorage is accessible via JavaScript and vulnerable to XSS attacks. HttpOnly cookies prevent JavaScript access entirely. Cookies require CSRF protection, but this is a manageable trade-off.
What happens when a JWT expires? When a JWT’s exp claim is in the past, the server rejects it with a 401 Unauthorized response. The client must use a refresh token to obtain a new access token, or prompt the user to authenticate again.
What is a JWT claim? A claim is a statement about the subject of the token. For example, "sub": "user_123" claims that the token’s subject is user 123. "role": "admin" claims that user is an admin. Claims are defined by RFC 7519 (registered claims) or by the application (private claims).