WEB API's Lesson 21 – JWT Authentication | Dataplexa
Web APIs · Lesson 21

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.

Why JWT Matters
Slack processes billions of API calls across their distributed infrastructure. Each request carries a JWT token that any Slack server can validate instantly. No database lookups. No shared session storage. Just cryptographic verification that happens in microseconds.

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.
1
Header: Contains token type and signing algorithm

2
Payload: Holds user data and claims

3
Signature: Verifies token authenticity and prevents tampering

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

Concept
Stateless Token
Used for
RFC 7519
Production Ready

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.

Token Size Matters
Keep JWT payloads small. Every claim adds bytes that travel with every request. Netflix learned this when their JWT tokens grew so large they caused mobile app performance issues. Strip unnecessary claims and use short property names for custom data.

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);
Generated token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImlzcyI6ImFwaWZvcmdlLmlvIn0.eyJzdWIiOiJkZXZfMTI4NDciLCJlbWFpbCI6InNhcmFoQHRlY2hzdGFydHVwLmlvIiwicm9sZXMiOlsiZGV2ZWxvcGVyIiwidGVhbV9sZWFkIl0sInBsYW4iOiJwcm8iLCJ0ZWFtX2lkIjoidGVhbV84NDkiLCJpYXQiOjE3MDk2NDcyMDAsImV4cCI6MTcwOTczMzYwMH0.kH8yGQNH7UAqB8Vx2vF1pMcRtDNKjAqPE7VnX8P2R1Y
What just happened?
The JWT library created a token containing Sarah's developer profile and permissions. The server signed it using HMAC SHA-256 with a secret key. Any APIForge service can now verify this token and extract her roles without touching the database. Try this: Decode the token at jwt.io to see the embedded claims in readable JSON format.

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' });
  }
}
// Token verification result { "decoded": { "sub": "dev_12847", "email": "sarah@techstartup.io", "roles": ["developer", "team_lead"], "plan": "pro", "team_id": "team_849", "iat": 1709647200, "exp": 1709733600, "iss": "apiforge.io" } }
What just happened?
The middleware extracted the Bearer token from the Authorization header, verified its signature against the secret key, and decoded the user claims. The request now carries authenticated user information for downstream authorization logic. Try this: Test with an expired token to see how the middleware handles different error types.

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.

Critical Security Flaw
Setting algorithm to "none" disables signature verification entirely. This debugging feature became a production nightmare when developers forgot to remove it. Uber discovered this vulnerability in their systems and immediately patched all services to explicitly require signature validation.

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' });
  }
});
POST /api/auth/refresh Content-Type: application/json { "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "expiresIn": 900 } HTTP/1.1 200 OK Content-Type: application/json Cache-Control: no-store Pragma: no-cache
What just happened?
The refresh endpoint validated the long-lived refresh token against database records, then generated a fresh 15-minute access token with current user permissions. The client can now make authenticated requests for another quarter-hour without user intervention. Try this: Implement automatic refresh logic in your frontend that triggers when access tokens near expiration.

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.

Production Tip
Rotate refresh tokens on every use. When a client exchanges a refresh token for new access token, issue a new refresh token too. This limits the window of exposure if refresh tokens get compromised and provides better audit trails.

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.

The APIForge Security team now understands how JWT tokens carry user identity and permissions in a cryptographically signed package. They can implement stateless authentication that scales horizontally while maintaining security through proper signing, verification, and refresh token patterns. Modern applications from Slack to Spotify rely on this foundation for authentication at 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?

Up Next
CORS
APIForge tackles cross-origin resource sharing to enable secure browser-based API access