JSON Web Tokens (JWT) for Secure Authentication
JSON Web Tokens (JWTs) provide a compact, URL-safe method for securely transmitting information between parties as a JSON object. They enable stateless authentication in web applications, eliminating the need for server-side session storage while maintaining security through cryptographic signatures.
Key Points
- Self-contained: JWTs carry all necessary information within the token itself
- Stateless: No server-side storage required, improving scalability
- Cryptographically signed: Ensures data integrity and authenticity
- URL-safe: Can be easily transmitted in HTTP headers, URLs, or POST parameters
- Widely adopted: Standard for OAuth 2.0, OpenID Connect, and modern APIs
JWT Structure
A JWT consists of three Base64Url-encoded parts separated by dots (.):
| Component | Purpose | Example Content |
|---|---|---|
| Header | Specifies token type and signing algorithm | {"alg": "HS256", "typ": "JWT"} |
| Payload | Contains claims (user data and metadata) | {"sub": "user123", "exp": 1735689600} |
| Signature | Verifies token integrity and authenticity | HMACSHA256(header.payload, secret) |
Important: The header and payload are Base64-encoded (readable when decoded), not encrypted. The signature prevents tampering but doesn't hide data.
Example JWT:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMTIzIiwiZXhwIjoxNzM1Njg5NjAwfQ.signature_hash
How JWTs Work
Token Creation
- User authenticates with credentials (username/password)
- Server validates credentials and generates a JWT containing:
- Header: Algorithm and token type
- Payload: User claims (ID, roles, expiration)
- Signature: Cryptographic hash of header + payload + secret key
- Server sends JWT to client (typically in response body or cookie)
Token Usage
- Client stores the JWT (in memory, localStorage, or cookies)
- Client includes JWT in the
Authorizationheader for subsequent requests:Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... - Server validates the signature and extracts claims
- Server grants or denies access based on token validity and claims
Token Validation
The server must verify:
- Signature integrity: Token hasn't been modified
- Expiration (
expclaim): Token is still valid - Not before (
nbfclaim): Token is active (if present) - Issuer (
issclaim): Token came from trusted source - Audience (
audclaim): Token is intended for this service
Signing Algorithms
Symmetric vs. Asymmetric
| Type | Algorithm | Key Usage | Best For | Security Level |
|---|---|---|---|---|
| Symmetric | HS256, HS384, HS512 | Single shared secret key | Internal services, monolithic apps | Good (if secret protected) |
| Asymmetric | RS256, RS512, ES256 | Private key signs, public key verifies | Distributed systems, public APIs | Better (key separation) |
Algorithm Selection Guidelines
Use HS256 (HMAC) when:
- Single application controls both token creation and validation
- Simpler key management is acceptable
- Performance is critical (HMAC is faster)
Use RS256 (RSA) when:
- Multiple services need to verify tokens
- Token issuer and validators are separate entities
- Public key distribution is feasible
- Enhanced security is required
Critical: Always explicitly specify the algorithm. Never allow the
nonealgorithm, which disables signature verification.
Security Best Practices
Essential Do's
- Always verify signatures: Never trust token contents without validation
- Use HTTPS exclusively: Prevent token interception during transmission
- Set short expiration times: Limit
expto 15-60 minutes for access tokens - Implement refresh tokens: Use long-lived refresh tokens to obtain new access tokens
- Validate all claims: Check
exp,iss,aud, and custom claims - Use strong secrets: Minimum 256 bits for HMAC algorithms
- Rotate signing keys: Periodically update keys and support multiple active keys
Critical Don'ts
- Never store sensitive data: Avoid passwords, credit cards, or PII in payload
- Don't use weak algorithms: Avoid
HS256with weak secrets ornone - Don't skip expiration: Always include
expclaim - Don't trust client data: Validate all claims server-side
- Don't expose secrets: Keep signing keys secure and out of version control
Secure Storage Options
| Storage Method | Security | XSS Vulnerable | CSRF Vulnerable | Best For |
|---|---|---|---|---|
| HttpOnly Cookie | High | No | Yes (use CSRF tokens) | Web applications |
| Memory (JavaScript) | Medium | Yes | No | Single-page apps (short sessions) |
| localStorage | Low | Yes | No | Avoid for sensitive tokens |
| sessionStorage | Medium | Yes | No | Temporary sessions only |
Recommendation: Use
HttpOnly,Secure, andSameSitecookies for web applications to maximize protection.
Token Lifecycle Management
Issuance
Generate tokens with minimal, necessary claims:
{
"sub": "user123",
"iat": 1735603200,
"exp": 1735606800,
"iss": "https://auth.example.com",
"aud": "https://api.example.com",
"scope": "read:profile write:posts"
}
Standard claims:
sub(subject): User identifieriat(issued at): Token creation timestampexp(expiration): Token expiration timestampiss(issuer): Token creatoraud(audience): Intended recipientscope: Permissions granted
Refresh Token Pattern
- Issue short-lived access token (15 minutes)
- Issue long-lived refresh token (7-30 days)
- Store refresh token securely (HttpOnly cookie or secure database)
- When access token expires, use refresh token to obtain new access token
- Rotate refresh tokens on each use for enhanced security
Revocation Strategies
Option 1: Short Expirations (Recommended)
- Set brief
exptimes (15-60 minutes) - Tokens automatically become invalid
- No server-side tracking needed
Option 2: Token Denylist
- Maintain server-side list of revoked token IDs (
jticlaim) - Check denylist on each request
- Requires storage and lookup overhead
Option 3: Version Claims
- Include user version number in token
- Increment version on logout/password change
- Reject tokens with outdated versions
Real-World Applications
Single Sign-On (SSO)
Scenario: User logs in once and accesses multiple applications
- Authentication service issues JWT after login
- User presents JWT to different services
- Each service validates JWT using shared public key
- No need to re-authenticate
API Authentication
Scenario: Mobile app communicating with backend API
1. User logs in → Server returns JWT
2. App stores JWT securely
3. App includes JWT in API requests:
GET /api/profile
Authorization: Bearer eyJhbGci...
4. API validates JWT and returns data
Microservices Communication
Scenario: Service-to-service authentication
- API Gateway issues JWT after user authentication
- JWT includes user context and permissions
- Internal services validate JWT without calling auth service
- Enables stateless, scalable architecture
OAuth 2.0 and OpenID Connect
- Access tokens: Often implemented as JWTs
- ID tokens: Always JWTs in OpenID Connect