In an interconnected digital economy, your APIs are the front doors to your business data and intellectual property. Whether serving mobile apps, third-party developer integrations, or internal microservices, unsecured API endpoints represent the single largest attack surface for data breaches. This handbook breaks down the three foundational authentication mechanisms—JSON Web Tokens (JWT), OAuth2, and Cryptographic API Keys—and outlines defense-in-depth API hardening patterns.
Authentication Mechanisms Compared: When to Use Which
| Authentication Pattern | State Characteristics | Optimal Deployment Scenario |
|---|---|---|
| JWT (Bearer Tokens) | Stateless; claims cryptographically signed by private key | Single-page web applications (SPAs) and first-party mobile apps |
| OAuth2 / OIDC | Delegated authorization protocol with scoped permissions | Third-party integrations (e.g., "Sign in with Google", external developer APIs) |
| HMAC-Signed API Keys | Secret shared between server and client; requests signed with timestamp hash | Server-to-server webhook delivery and B2B programmatic API consumption |
The Short-Lived Access Token + Refresh Token Rotation Pattern
A critical flaw in naive JWT implementations is issuing tokens valid for 30 days. If an attacker intercepts the token, they possess unfettered account access for a month with zero revocation mechanism.
The Secure Pattern: Issue **short-lived Access Tokens (valid for 10–15 minutes)** coupled with **Refresh Tokens (valid for 7 days)** stored in HttpOnly cookies with automatic rotation:
// auth/tokenService.ts
import jwt from 'jsonwebtoken';
import { redis } from '@/lib/redis';
export async function rotateRefreshToken(oldRefreshToken: string, userId: string) {
// 1. Check if token was already used (Reuse Detection / Replay Attack!)
const isRevoked = await redis.get(`revoked_token:${oldRefreshToken}`);
if (isRevoked) {
// SECURITY ALERT: Compromised token reused! Invalidate ALL user sessions immediately!
await redis.del(`user_sessions:${userId}`);
throw new Error('Security Breach: Token reuse detected. Session invalidated.');
}
// 2. Blacklist the consumed refresh token in Redis
await redis.set(`revoked_token:${oldRefreshToken}`, '1', 'EX', 60 * 60 * 24 * 7);
// 3. Issue brand new token pair
const newAccessToken = jwt.sign({ userId }, process.env.JWT_ACCESS_SECRET!, { expiresIn: '15m' });
const newRefreshToken = jwt.sign({ userId }, process.env.JWT_REFRESH_SECRET!, { expiresIn: '7d' });
return { newAccessToken, newRefreshToken };
}
Defense-in-Depth API Security Checklist
- Input Sanitization & Schema Validation: Validate 100% of incoming payloads using Zod or Pydantic before business logic execution. Reject unrecognized keys.
- Sliding-Window Rate Limiting: Enforce strict rate limits per IP and per authenticated user ID via Redis to neutralize brute-force credential stuffing and scraping.
- Disable Verbose Stack Traces: In production environments, never leak database error strings or file paths in API 500 error responses. Return standardized error codes (e.g.,
ERR_INTERNAL_SERVER). - CORS Preflight Caching: Cache preflight OPTIONS responses for 24 hours to reduce latency while restricting allowed origins strictly to verified subdomains.
Frequently Asked Questions
Why should API keys use SHA-256 hashing in the database?
Never store plaintext API keys in your database. Just like user passwords, store a cryptographic hash (e.g., sha256(api_key)). When a client presents an API key, hash it and compare against the database. If your database is compromised, attackers cannot steal valid API keys.
How does HMAC webhook verification work?
The webhook sender generates a SHA-256 HMAC hash of the raw JSON payload using a shared secret key, sending it in an HTTP header (e.g., X-Signature). The recipient hashes the incoming payload with the same secret and verifies matching signatures before processing.
What is the difference between symmetric and asymmetric JWT signing?
Symmetric signing (HS256) uses a single shared secret string to both sign and verify tokens. Asymmetric signing (RS256 / EdDSA) uses a private key to sign tokens and a public key to verify them, allowing microservices to verify tokens without knowing the master signing secret.
Need Help Implementing This in Production?
Our senior engineering team helps startups and enterprises scale web applications, migrate cloud infrastructure, and build custom AI workflows with guaranteed delivery timelines.