API Authentication Guide: JWT, OAuth 2.0, API Keys & mTLS
Choosing the right authentication mechanism for your API depends on who the caller is, where tokens are stored, and what security guarantees you need. This guide walks through every major strategy with implementation examples and a comparison table.
JWT: Structure, Validation & Refresh Tokens
A JWT consists of three base64url-encoded parts: header (algorithm), payload (claims), and signature. Validate the signature and claims on every request:
import jwt from 'jsonwebtoken';
import { Request, Response, NextFunction } from 'express';
// Issue tokens — short-lived access + long-lived refresh
function issueTokens(userId: string, roles: string[]) {
const accessToken = jwt.sign(
{ sub: userId, roles, type: 'access' },
process.env.JWT_SECRET!,
{ expiresIn: '15m', issuer: 'devplaybook.cc', audience: 'api' }
);
const refreshToken = jwt.sign(
{ sub: userId, jti: crypto.randomUUID(), type: 'refresh' },
process.env.REFRESH_SECRET!,
{ expiresIn: '7d' }
);
return { accessToken, refreshToken };
}
// Middleware — validates Bearer token on every protected request
export function requireAuth(req: Request, res: Response, next: NextFunction) {
const token = req.headers.authorization?.replace('Bearer ', '');
if (!token) return res.status(401).json({ error: 'Missing token' });
try {
const payload = jwt.verify(token, process.env.JWT_SECRET!, {
issuer: 'devplaybook.cc',
audience: 'api',
}) as any;
if (payload.type !== 'access') throw new Error('Wrong token type');
req.user = { id: payload.sub, roles: payload.roles };
next();
} catch (err) {
res.status(401).json({ error: 'Invalid or expired token' });
}
} OAuth 2.0 Client Credentials Flow (M2M)
For machine-to-machine authentication (background jobs, microservices), use the OAuth 2.0 Client Credentials grant — no user involved:
# Step 1: Client requests access token using client_id + client_secret
POST /oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
&client_id=svc-orders
&client_secret=cs_live_abc123
&scope=users:read orders:write
# Step 2: Authorization server responds with short-lived access token
{
"access_token": "eyJhbGci...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "users:read orders:write"
}
# Step 3: Service uses the token as Bearer in API calls
GET /api/v1/users
Authorization: Bearer eyJhbGci...
# Client implementation — cache token, refresh before expiry
class OAuth2Client {
private token: string | null = null;
private expiresAt = 0;
async getToken(): Promise<string> {
if (this.token && Date.now() < this.expiresAt - 30_000) return this.token;
const res = await fetch(process.env.TOKEN_URL!, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({ grant_type: 'client_credentials',
client_id: process.env.CLIENT_ID!, client_secret: process.env.CLIENT_SECRET!,
scope: 'users:read' }),
});
const data = await res.json();
this.token = data.access_token;
this.expiresAt = Date.now() + data.expires_in * 1000;
return this.token!;
}
} mTLS: Mutual TLS for Service-to-Service
mTLS requires both client and server to present certificates. Zero tokens to manage — the TLS handshake IS the authentication:
import https from 'https';
import fs from 'fs';
// Server: require client certificates
const serverOptions = {
key: fs.readFileSync('./certs/server.key'),
cert: fs.readFileSync('./certs/server.crt'),
ca: fs.readFileSync('./certs/ca.crt'), // CA that issued client certs
requestCert: true, // Request client cert
rejectUnauthorized: true, // Reject if cert is invalid
};
https.createServer(serverOptions, app).listen(8443);
// In Express middleware — extract client identity from cert
app.use((req: any, res, next) => {
const cert = req.socket.getPeerCertificate();
if (!cert.subject) return res.status(401).send('Client cert required');
req.serviceId = cert.subject.CN; // e.g. 'orders-service'
next();
});
# Client: present its own certificate when calling the server
# curl --cert client.crt --key client.key --cacert ca.crt https://api:8443/endpoint Authentication Method Comparison
| Method | Stateless | Revocable | Best For | Complexity |
|---|---|---|---|---|
| API Key | No (DB lookup) | Yes (delete from DB) | Server-to-server, SDKs | Low |
| JWT (Bearer) | Yes | Partial (via blocklist) | User auth, microservices | Medium |
| OAuth 2.0 (CC) | Yes (JWT tokens) | Yes (revoke refresh) | M2M, background services | Medium |
| OAuth 2.0 (Auth Code) | Yes | Yes | User-facing web/mobile apps | High |
| mTLS | Yes (cert-based) | Yes (CRL / OCSP) | Zero-trust, internal services | High |
Related Products
on GumroadDevPlaybook Interview Vault
210+ real technical interview questions with model answers, asked at Google, Meta, Amazon, Microsoft, Stripe, and Shopify. Covers Frontend (55), Backend (55), System Design (50), and DSA (50). Available in English and Traditional Chinese.
Buy on Gumroad →TypeScript Quick Reference Toolkit
5 practical reference files for TypeScript developers: cheatsheet, TS vs JS comparison, 20 utility types with real examples, tsconfig best practices template, and 15 common mistakes guide. Print it. Keep it on your desk.
Buy on Gumroad →Developer Interview Vault
210 technical interview questions with model answers, key points, and follow-up questions. Covers Frontend, Backend, System Design, and DSA — bilingual English + Traditional Chinese (ZH-TW). PWYW from $29.
Buy on Gumroad →You Might Also Need
JWT Decoder
Free JWT decoder. Inspect any JSON Web Token
Open free tool →
OpenAPI Validator
Paste your OpenAPI 3.x spec (YAML or JSON) to validate structure, find missing required fields, detect duplicate paths, check for missing operationIds, and get errors and warnings with path references.
Open free tool →
API Response Mock Generator
Generate realistic mock API responses with custom status codes, headers, and JSON body. Quick templates for 200, 401, 404, 429, 500 and more. Runs in your browser.
Open free tool →
More Free Tools
Related Articles
JWT Authentication Best Practices 2026: Secure Your API Tokens
Complete JWT security guide for 2026. Covers RS256 vs HS256 signing, token storage strategies, expiry and refresh token patterns, revocation, common vulnerabilities, and a production security checklist.
Web Authentication Patterns in 2026: OAuth, Passkeys, and JWT Best Practices
A complete guide to modern web authentication: OAuth 2.0/OIDC, Passkeys/WebAuthn, JWT best practices, session management, and MFA strategies for secure apps.
JWT Token Decoder: How to Debug Authentication Issues
Learn how to use a JWT token decoder to diagnose 401 errors, expired tokens, missing claims, and auth misconfiguration. Practical debugging guide with code examples.
JWT Tokens Explained: How to Decode and Debug Them
Understand JSON Web Tokens from header to signature. Learn how to decode JWTs, read their claims, debug authentication issues, and use free online JWT tools.
JWT Decoder: How to Read Token Payloads
Learn how to decode JWT tokens and read their payloads. A practical guide to JWT structure, claims, expiry checking, and debugging auth issues with a free online decoder.
Enjoying this tool? Get more like it.
Weekly developer tool guides, productivity playbooks & AI tricks. Free. No spam.
Unlock AI-Powered Dev Tools
- ⚡ AI Code Review, Doc Generator & SQL Builder
- ⚡ All premium templates & early access
- ⚡ Member discounts on Gumroad products
$9/mo after trial · Cancel anytime
Want the full toolkit?
Get DevPlaybook Pro
Every template, guide, boilerplate, and automation script in one bundle. 13 premium products — grab them all at once and save big.
MIT licensed · Instant download · No subscription
See what's included → Browse all deals →Frequently Asked Questions
What is the difference between authentication and authorization?▼
Authentication (authn) answers 'Who are you?' — verifying the identity of the caller via credentials (password, token, certificate). Authorization (authz) answers 'What can you do?' — checking if the authenticated identity has permission to perform the requested operation. An API key authenticates a client; scope claims in a JWT authorize what that client can access. Always separate these concerns in your code.
When should I use API keys vs JWT?▼
Use API keys for server-to-server integrations where the client is a machine, the key is stored securely, and simplicity is valued over granularity. Use JWTs for user-facing authentication where you need to embed claims (user ID, roles, expiry) in the token itself without a database lookup on every request. JWTs are stateless; API keys require a database lookup to validate unless you use a hash-based scheme.
What are the OAuth 2.0 grant types and when should I use each?▼
Client Credentials: machine-to-machine, no user involved — use for background jobs and service accounts. Authorization Code (+ PKCE): user-facing apps — browser or mobile app redirects to identity provider, user logs in, app gets a short-lived code it exchanges for tokens. Implicit flow is deprecated. Device Code: TV or CLI apps without browsers. Refresh Token: exchange a long-lived refresh token for a new access token without re-authentication.
How long should JWT access tokens live?▼
Access tokens should be short-lived — typically 5 to 60 minutes. Short expiry limits the damage if a token is stolen. Pair short-lived access tokens with longer-lived refresh tokens (hours to days) that are rotated on use (refresh token rotation). Store refresh tokens securely: httpOnly cookies for web apps, secure storage (Keychain/Keystore) for mobile apps. Never store access or refresh tokens in localStorage.
What is mTLS and when is it used?▼
Mutual TLS (mTLS) is a form of authentication where both the client and server present X.509 certificates. Unlike standard TLS (only the server authenticates itself), mTLS verifies the client's identity at the TLS layer before any HTTP request is processed. It is used for service-to-service authentication in zero-trust architectures, Kubernetes pods using SPIFFE/SPIRE, and payment APIs. It provides very strong authentication without managing token expiry.
How do I revoke a JWT if it's stateless?▼
JWTs cannot be revoked without some server-side state. Common strategies: (1) Short expiry + refresh token rotation — a stolen access token expires quickly; revoke the refresh token in the database. (2) Token blocklist — maintain a Redis set of revoked JTI (JWT ID) claims; check on each request (adds a Redis lookup). (3) Token versioning — store a version number per user; JWTs include the version; increment the version to invalidate all existing tokens.