What is JWT? From Concept to Structure Explained
A JSON Web Token (JWT) is a compact, URL-safe means of representing claims to be transferred between two parties. Defined in RFC 7519, JWTs are the backbone of modern stateless authentication.
The Three-Part Structure
A JWT consists of three Base64URL-encoded parts separated by dots:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0IiwibmFtZSI6IkpvaG4iLCJpYXQiOjE1MTYyMzkwMjJ9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
HEADER PAYLOAD SIGNATUREHeader
{
"alg": "HS256",
"typ": "JWT"
}Specifies the signing algorithm and token type.
Payload (Claims)
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022,
"exp": 1516242622
}Contains the claims — statements about the user and metadata. The payload is only Base64URL-encoded, not encrypted. Anyone who has the token can read it. Never put passwords, API keys, or other secrets in claims.
| Claim | Name | Description |
|---|---|---|
| `iss` | Issuer | Who created the token |
| `sub` | Subject | Who the token refers to |
| `aud` | Audience | Who the token is intended for |
| `exp` | Expiration | When the token expires (Unix timestamp) |
| `iat` | Issued At | When the token was created |
| `nbf` | Not Before | Token is invalid before this time |
| `jti` | JWT ID | Unique identifier for the token |
You can add custom claims like role or tenantId. Keep the payload small — JWTs travel with every request.
Signature
HMAC-SHA256(
base64url(header) + "." + base64url(payload),
secret
)The signature ensures the token hasn't been tampered with. Only someone with the secret key can produce a valid signature.
How JWT Authentication Works
1. User logs in with credentials
2. Server validates credentials, creates a JWT signed with its secret
3. Server returns the JWT to the client
4. Client includes the JWT in subsequent requests: Authorization: Bearer
5. Server validates the signature and extracts claims — no database lookup needed
This stateless design is why JWTs scale so well in distributed systems.
Sign and Verify (Node.js)
const jwt = require('jsonwebtoken');
const token = jwt.sign(
{ sub: '123', role: 'user' },
process.env.JWT_SECRET,
{ algorithm: 'HS256', expiresIn: '1h' }
);
const payload = jwt.verify(token, process.env.JWT_SECRET, {
algorithms: ['HS256'],
});Generate a strong secret with the JWT Secret Generator, build tokens with the JWT Encoder, and inspect them with the JWT Validator.
Where to Store Tokens on the Client
| Storage | XSS Risk | CSRF Risk | Recommendation |
|---|---|---|---|
| localStorage | High | Low | Avoid for auth tokens |
| sessionStorage | High | Low | Avoid for auth tokens |
| Memory (JS variable) | Medium | Low | OK for SPA access tokens during the session |
| httpOnly cookie | Low | Medium | Best default — add CSRF protection |
For SPAs, a common pattern is httpOnly Secure SameSite cookies for refresh tokens and short-lived access tokens in memory. See localStorage vs cookies for JWTs.
Common Beginner Mistakes
1. Trusting decoded tokens without verification — jwt.decode() does not check the signature
2. Storing sensitive data in the payload — JWTs are readable by anyone who has the token
3. No expiration — tokens without exp remain valid forever
4. Accepting alg: none — never accept unsigned tokens in production
5. Sharing secrets between environments — use separate keys for dev, staging, and prod
When JWTs Are Not the Right Choice
JWTs excel at stateless authentication between services, but they are not always ideal:
- Session-heavy apps that need immediate revocation may prefer server-side sessions
- Opaque tokens in Redis give instant revoke without waiting for expiry
- Some first-party mobile apps use refresh-token-only flows with very short access tokens
HS256 vs RS256
- HS256 — one shared secret signs and verifies. Simple and fast for monoliths.
- RS256 — private key signs, public key verifies. Better when many services must verify without sharing the signing key.
See HS256 vs RS256 when you are ready to choose. For production hygiene, follow the JWT Best Practices Checklist.