Overview
An API authentication method is how a client proves its identity on each request. The common options are Basic authentication (username and password on every request, only over HTTPS), API keys (a long random secret identifying an application), bearer tokens such as JWTs or opaque tokens (issued after login and sent in the Authorization header), OAuth 2.0 (delegated access tokens for users and third-party apps), and mutual TLS (both client and server present certificates).
The right choice depends on who the caller is. Server-to-server integrations often use API keys or OAuth client credentials; user-facing apps use OAuth/OIDC tokens or sessions; high-security internal traffic uses mTLS. Most production APIs combine methods and add scopes, rotation, and rate limits per credential.
Basic auth is saying your password at the door every time. An API key is a company badge. A bearer token is a day pass from reception that expires tonight. OAuth is a valet key that lets someone use your car but not open the trunk. mTLS is both guard and visitor checking each other's government ID.
When to use it
- Designing how clients call your API.
- Choosing credentials for partners, mobile apps, and internal services.
- Security reviews of existing APIs.
Where it shows up in interviews
Recognize it when: how will clients authenticate to the API?
- Design a public developer API
- Design authentication for a mobile app backend
Recognize it when: internal services calling each other.
- Design zero-trust microservices
- Design a partner integration platform
Where it is used in real software
Use secret API keys in the Authorization header, with separate test and live keys and restricted-scope keys.
Use OAuth 2.0 access tokens with scopes for user-delegated access.
Istio and Linkerd use mTLS to authenticate every service-to-service call automatically.
Key terms
- Basic auth
- Base64-encoded username:password in the Authorization header.
- API key
- Long random secret identifying a client application.
- Bearer token
- Token whose possession grants access (Authorization Bearer header).
- Scope
- Limits what a token or key is allowed to do.
- mTLS
- TLS where the client also presents a certificate.
How it works, step by step
- 1Identify the caller type
End user, third-party app, partner server, or internal service.
- 2Pick the method
OAuth/OIDC for users and delegated access, API keys or client credentials for servers, mTLS for internal traffic.
- 3Send credentials safely
Only over HTTPS, in the Authorization header, never in URLs or logs.
- 4Store and verify securely
Hash API keys at rest; validate token signature, expiry, audience, and scopes.
- 5Rotate and revoke
Support multiple active keys, short-lived tokens, and immediate revocation.
Method comparison
Choose by caller and risk
| Method | Best for | Strengths | Weaknesses |
|---|---|---|---|
| Basic auth | Simple internal tools | Trivial to implement | Password sent on every request |
| API key | Server-to-server, developer APIs | Simple, per-app tracking and limits | Long-lived secret; no user context |
| Bearer token (JWT/opaque) | Logged-in users and apps | Short-lived, scoped | Must protect from theft |
| OAuth 2.0 | Third-party and delegated access | No password sharing, scopes, consent | More moving parts |
| mTLS | Internal service-to-service | Strong mutual identity | Certificate management overhead |
NOWMethod: Basic auth | Best for: Simple internal tools | Strengths: Trivial to implement | Weaknesses: Password sent on every request
Match the method to who is calling; combine with scopes, rate limits, and rotation.
Implementation
import { createHash, timingSafeEqual } from "node:crypto"; // API keys: store only a hash, show the full key once at creationconst hash = (key: string) => createHash("sha256").update(key).digest(); async function authenticate(req: Request): Promise<{ clientId: string; scopes: string[] }> { const header = req.headers.get("authorization") ?? ""; const [scheme, credential] = header.split(" "); if (scheme === "Bearer" && credential?.startsWith("sk_")) { const record = await apiKeys.findByPrefix(credential.slice(0, 12)); // prefix index, not the secret if (record && timingSafeEqual(record.hash, hash(credential)) && !record.revoked) { return { clientId: record.clientId, scopes: record.scopes }; } } if (scheme === "Bearer") { const claims = await verifyJwt(credential, { audience: "orders-api", issuer: "https://auth.example.com" }); return { clientId: claims.sub, scopes: String(claims.scope ?? "").split(" ") }; } throw new HttpError(401, "UNAUTHENTICATED", "Missing or invalid credentials");} function requireScope(scopes: string[], needed: string) { if (!scopes.includes(needed)) throw new HttpError(403, "FORBIDDEN", `Scope ${needed} required`);}Complexity and performance
Cache verified keys briefly.
Revocation needs short expiry or a denylist.
Trade-offs
JWTs avoid a database lookup per request but are hard to revoke before expiry; opaque tokens need introspection but revoke instantly.
API keys are simple but long-lived and often leaked; OAuth and mTLS are stronger but add infrastructure.
Variants and related techniques
Clients sign each request with a secret (AWS SigV4), preventing tampering and replay.
OpenID Connect adds user identity (ID tokens) on top of OAuth 2.0.
Common mistakes
- API keys in query strings or client-side code.
Fix: Send in headers; keep secret keys server-side only; use publishable keys for browsers.
- Storing API keys in plain text.
Fix: Store hashes and show the full key only once.
- Long-lived tokens without scopes.
Fix: Short expiry, least-privilege scopes, and refresh tokens.
Interview questions
API key vs OAuth token?
An API key identifies an application with a long-lived secret and suits server-to-server calls. An OAuth access token is short-lived, scoped, and can represent a user's delegated consent, which suits third-party and user-facing access.
How do you secure API keys?
Generate long random keys, show them once, store only hashes, send them in headers over HTTPS, scope them, rate limit per key, support rotation with overlapping validity, and revoke immediately on leaks (secret scanning helps detect them).
Practice problems
| Problem | Difficulty | What it trains |
|---|---|---|
| Choose auth methods for 5 client types | Easy | Matching method to caller. |
| Design API key issuance, rotation, and revocation | Medium | Key lifecycle. |