Web APIs
JWT Authentication
Build secure stateless authentication using JSON Web Tokens for modern API systems.
GitHub processes millions of API requests every hour. No sessions. No server-side state. Just tiny encrypted tokens that carry user identity and permissions. That token mechanism is JWT, and it changed how modern APIs handle authentication.Traditional session-based authentication stores user data on the server. Your API remembers who you are by keeping a record in memory or a database. But this approach breaks down when you scale across multiple servers or need to authenticate requests from mobile apps, third-party integrations, and microservices.
JWT solves this by embedding authentication data directly in the token itself. The server generates a cryptographically signed token containing user information and permissions. Every subsequent request includes this token, allowing any server to verify the user without checking a central database.
JWT Token Structure
A JWT token looks like random gibberish, but it contains three distinct parts separated by dots. Each part serves a specific purpose in the authentication process.The header tells the receiving server which algorithm was used to create the token signature. The payload contains the actual user information and permissions. The signature proves the token came from a trusted source and hasn't been modified.
Each part gets encoded using Base64URL encoding, making the token safe to include in URLs and HTTP headers. The complete token might look like: eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxMjN9.signature_hash
JWT Claims and Payload
Claims are the heart of JWT authentication. They define who the user is, what they can access, and when the token expires.Standard claims follow RFC 7519 specifications. The iss claim identifies who issued the token. The sub claim specifies the subject (usually user ID). The exp claim sets when the token expires.
Custom claims let you embed application-specific data. You might include user roles, subscription levels, or feature flags directly in the token. This eliminates database lookups during authorization checks.
| Claim | What it does | APIForge use case |
|---|---|---|
sub |
Identifies the user this token represents | Developer account ID for API access |
exp |
Unix timestamp when token becomes invalid | 24-hour expiry for security tokens |
iat |
Records when the token was created | Audit trails and token refresh logic |
roles |
Custom claim for user permissions | Team admin vs developer permissions |
plan |
Subscription tier information | Rate limit enforcement without database hits |
Spotify embeds user subscription status directly in JWT tokens. When you request a high-quality audio stream, their servers check the plan claim in your token instantly. No database query needed to determine if you have Premium access.
JWT Generation and Signing
Creating secure JWT tokens requires proper signing algorithms and secret management. The signing process proves token authenticity without revealing the secret key.HMAC SHA-256 (HS256) uses a shared secret key that both the issuer and verifier must know. This works well for applications where the same server both creates and validates tokens. The secret never travels with the token.
RSA SHA-256 (RS256) uses public-key cryptography. The private key signs tokens, while public keys verify them. This enables distributed systems where multiple services need to validate tokens without sharing signing secrets.
// APIForge Security team generates JWT tokens for developer authentication
const jwt = require('jsonwebtoken');
const payload = {
sub: 'dev_12847',
email: 'sarah@techstartup.io',
roles: ['developer', 'team_lead'],
plan: 'pro',
team_id: 'team_849',
iat: Math.floor(Date.now() / 1000),
exp: Math.floor(Date.now() / 1000) + (24 * 60 * 60) // 24 hours
};
const secret = process.env.JWT_SECRET_KEY;
const token = jwt.sign(payload, secret, {
algorithm: 'HS256',
issuer: 'apiforge.io'
});
console.log('Generated token:', token);Secret key rotation prevents long-term security risks. Auth0 rotates their signing keys automatically and publishes public keys at a well-known endpoint. Applications fetch current keys to verify tokens without service interruption.
JWT Verification Process
Token verification happens on every protected API request. The process validates the signature, checks expiration, and extracts user claims for authorization decisions.Signature verification uses the same algorithm and secret that created the token. If someone tampered with the payload, the signature won't match and verification fails. This cryptographic proof ensures data integrity.
Expiration checking prevents replay attacks with stolen tokens. Even if an attacker intercepts a valid JWT, it becomes useless after the expiry timestamp. Most applications set short expiration times and provide refresh token mechanisms.
// APIForge middleware verifies JWT tokens on protected endpoints
const jwt = require('jsonwebtoken');
function verifyToken(req, res, next) {
const authHeader = req.headers.authorization;
const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN
if (!token) {
return res.status(401).json({ error: 'Access token required' });
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET_KEY);
req.user = decoded; // Attach user claims to request
next();
} catch (error) {
if (error.name === 'TokenExpiredError') {
return res.status(401).json({ error: 'Token expired' });
}
return res.status(403).json({ error: 'Invalid token' });
}
}JWT Security Considerations
JWT security depends on proper implementation and key management. Several common mistakes can compromise your entire authentication system.Never store sensitive data like passwords or payment information in JWT payloads. The payload is Base64-encoded, not encrypted. Anyone with the token can decode and read the claims. Only include data you're comfortable exposing to the client.
Algorithm confusion attacks exploit servers that accept multiple signing algorithms. An attacker might change the algorithm from RS256 to HS256, then use the public key as the HMAC secret. Always specify the expected algorithm explicitly during verification.
Token storage location affects security posture. Local storage persists across browser sessions but remains vulnerable to XSS attacks. HTTP-only cookies prevent JavaScript access but require CSRF protection. Memory storage provides better security but loses tokens on page refresh.
Short expiration times limit damage from compromised tokens. GitHub sets 1-hour expiry on their JWT tokens and provides refresh tokens for longer sessions. This approach balances security with user experience.
JWT vs Session Authentication
Understanding when to choose JWT over traditional sessions requires evaluating your application architecture and scalability needs.Session authentication stores user state on the server, typically in memory or a database. The client receives a session ID that references this server-side data. This approach works well for monolithic applications with sticky sessions but becomes complex in distributed environments.
JWT authentication embeds user state in the token itself. No server-side storage required. Any service can validate tokens independently, making JWT ideal for microservices architectures and API-first applications.
Session Authentication
• Server stores user state
• Immediate revocation possible
• Smaller network overhead
• Database dependency
JWT Authentication
• Stateless verification
• Horizontal scaling friendly
• Cross-service compatibility
• Larger payload size
Shopify uses JWT for their REST Admin API because partners integrate from diverse platforms and programming languages. The stateless nature eliminates session management complexity and enables automatic scaling across their global infrastructure.
Refresh Token Implementation
Refresh tokens solve the security versus usability dilemma by enabling long-lived authentication with short-lived access tokens.Access tokens carry user permissions and expire quickly, typically within 15 minutes to 1 hour. Refresh tokens last much longer but can only exchange for new access tokens. This two-token system limits attack surface while maintaining seamless user experience.
The refresh process happens automatically in the background. When an access token expires, the client presents the refresh token to get a new access token. Users stay authenticated without manual login steps.
// APIForge implements refresh token endpoint for seamless authentication
app.post('/api/auth/refresh', async (req, res) => {
const { refreshToken } = req.body;
try {
// Verify refresh token (stored in database)
const storedToken = await RefreshToken.findOne({
token: refreshToken,
expires: { $gt: new Date() }
});
if (!storedToken) {
return res.status(401).json({ error: 'Invalid refresh token' });
}
// Generate new access token
const accessToken = jwt.sign(
{
sub: storedToken.userId,
roles: storedToken.roles,
plan: storedToken.plan
},
process.env.JWT_SECRET_KEY,
{ expiresIn: '15m' }
);
res.json({ accessToken, expiresIn: 900 });
} catch (error) {
res.status(500).json({ error: 'Token refresh failed' });
}
});Discord stores refresh tokens in their database with device fingerprints and IP address tracking. When suspicious activity occurs, they can revoke specific refresh tokens while leaving other user sessions intact. This granular control provides security without frustrating legitimate users.
JWT in Modern Applications
Real-world JWT implementation goes beyond basic authentication to enable sophisticated authorization patterns and cross-service communication.Microservices architectures rely heavily on JWT for service-to-service authentication. Each service can validate tokens independently without calling a central authentication service. This eliminates single points of failure and reduces network latency.
Progressive Web Apps use JWT to maintain authentication state across offline periods. The token remains valid even when the device loses internet connectivity, allowing cached content to display with proper user context.
Serverless functions benefit from JWT statelessness. AWS Lambda functions can verify user tokens without maintaining persistent connections or shared state between invocations. This makes authentication seamless across serverless architectures.
Twilio embeds customer account limits directly in their JWT tokens. When you send an SMS through their API, the processing service checks your remaining credit balance from the token claims. No database lookup required, even under massive scale.
Quiz
1. The APIForge Security team needs to understand JWT token structure. What is the primary purpose of the signature component in a JWT token?
2. An APIForge developer's access token expires after 15 minutes during API integration testing. What should happen next to maintain authentication?
3. APIForge is scaling to a microservices architecture with multiple API servers. Why would JWT be preferable to session-based authentication in this scenario?