API Authentication Methods: A Practical Comparison
Picking the right authentication mechanism affects security, developer experience, and operational complexity.
Basic Authentication
Sends base64-encoded username:password in every request header.
Authorization: Basic dXNlcjpwYXNzd29yZA==
Pros: Dead simple, universally supported, built into every HTTP client. Cons: Credentials on every request, no expiry, base64 is not encryption (requires HTTPS). Use when: Internal tools, CLI scripts, legacy system compatibility. Never for user-facing APIs.
API Keys
Static tokens issued per client.
// Client
fetch(url, { headers: { 'X-API-Key': process.env.API_KEY } });
// Server: validate hashed key
const hash = crypto.createHash('sha256').update(key).digest('hex');
const client = await db.query('SELECT * FROM api_keys WHERE hash = $1', [hash]);
Security practices: Store only SHA-256 hashes, prefix by environment (sk_live_, sk_test_), allow self-rotation, set rate limits per key.
Pros: Simple, stateless, easy for developers. Cons: No built-in expiry, limited scope granularity. Use when: Machine-to-machine, public developer APIs (Stripe/SendGrid pattern).
JWT (JSON Web Tokens)
Self-contained tokens with signed claims. Server validates signature — no database lookup needed.
// Issue on login
const token = jwt.sign(
{ sub: user.id, email: user.email, role: user.role },
process.env.JWT_SECRET,
{ algorithm: 'HS256', expiresIn: '15m' }
);
// Verify
req.user = jwt.verify(token, process.env.JWT_SECRET, { algorithms: ['HS256'] });
Pros: Stateless (scales horizontally), contains user data, short-lived by design. Cons: Cannot be revoked before expiry — keep access tokens short (5-15 min), use refresh tokens. Use when: User authentication in SPAs, mobile apps, microservices.
OAuth 2.0
Delegation framework — users grant third-party apps access without sharing credentials.
1. User clicks "Sign in with GitHub"
2. App redirects to GitHub's authorize endpoint
3. User approves the requested scopes
4. GitHub redirects back with authorization code
5. App exchanges code for access token
6. App uses token to call GitHub API on user's behalf
Grant types to use: Authorization Code + PKCE (web/mobile), Client Credentials (server-to-server). Avoid deprecated: Implicit grant, Resource Owner Password grant.
Pros: Industry standard, scoped permissions, users can revoke access. Cons: Complex to implement correctly, redirect flows add UX friction. Use when: "Sign in with Google/GitHub", third-party app integrations.
HMAC Signatures
Signs request body and timestamp — secret never transmitted.
function signRequest(method, path, body, secret) {
const timestamp = Math.floor(Date.now() / 1000).toString();
const payload = [method, path, timestamp, body].join('
');
const sig = crypto.createHmac('sha256', secret).update(payload).digest('base64');
return { 'X-Timestamp': timestamp, 'X-Signature': `hmac-sha256=${sig}` };
}
// Prevent replay attacks: reject if |timestamp - now| > 300 seconds
Use when: Webhook verification, financial APIs, when tamper-evident requests matter.
Comparison
| Method | Stateless | Revocable | Scoped | Complexity | Best for |
|---|---|---|---|---|---|
| Basic Auth | Yes | No | No | Very Low | Internal tools |
| API Key | Yes | Yes | Manual | Low | M2M, developer APIs |
| JWT | Yes | No (until expiry) | In token | Medium | User auth in APIs |
| OAuth 2.0 | Yes | Yes | Yes | High | Delegated access |
| HMAC Sig | Yes | Yes | No | Medium | Webhooks, financial APIs |
→ Decode and inspect JWT tokens with the JWT Parser.