
How API Authentication Works — API Keys, OAuth, JWT, and Sessions
Every API needs to answer two questions: who is making this request? (authentication) and are they allowed to do this? (authorization). Authentication proves identity. Authorization checks permissions. They are separate concerns, but authentication always comes first.
There are four common approaches, each with different tradeoffs between simplicity, security, and scalability.
API Keys — Simple, Limited
An API key is a long random string that identifies the caller. The client sends it in a header or query parameter with every request:
GET /api/data HTTP/1.1
Authorization: Bearer sk_live_abc123def456...
The server looks up the key in a database, finds which account it belongs to, and checks permissions.
Advantages:
- Simplest to implement — generate a random string, store its hash
- Easy for developers to use (copy-paste into config)
- Good for server-to-server communication where there's no user interaction
Limitations:
- No expiration by default — if leaked, it works until manually revoked
- Identifies the application, not the user (you can't tell which human made the request)
- No standard for scoping permissions (each API invents its own scheme)
- Must be transmitted over TLS — anyone who intercepts the key has full access
API keys are appropriate for internal services, developer tools, and third-party integrations where a human user isn't directly involved.
Session-Based Authentication — Server-Side State
Traditional web authentication: the user logs in with a username and password, the server creates a session, stores it in a database or Redis, and sends back a session ID in a cookie.
POST /login
{ "email": "[email protected]", "password": "..." }
→ Set-Cookie: session_id=abc123; HttpOnly; Secure; SameSite=Strict
Every subsequent request includes the cookie automatically. The server looks up the session ID, finds the user, and processes the request.
Advantages:
- Server controls everything — sessions can be revoked instantly
- HttpOnly cookies prevent JavaScript access (XSS protection)
- SameSite attribute prevents cross-site request forgery (CSRF)
- Well-understood, battle-tested pattern
Limitations:
- Server must store sessions — requires shared storage (Redis) in a distributed system
- Doesn't scale easily — every request requires a session lookup
- Cookies are browser-specific — mobile apps and APIs typically prefer tokens
- Cross-domain requests are complex (CORS + cookie policies)
Session-based auth is ideal for traditional web applications where the server and client share the same domain.
JWT — Stateless Tokens
A JSON Web Token (JWT) is a signed, self-contained token that carries claims about the user. The server doesn't need to store anything — the token itself contains the identity and permissions.
A JWT has three parts, base64-encoded and separated by dots:
header.payload.signature
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI0MiIsInJvbGUiOiJhZG1pbiIsImV4cCI6MTcxMTMyMTYwMH0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Header: algorithm and token type ({"alg": "HS256", "typ": "JWT"}).
Payload: claims — user ID, role, expiration time ({"sub": "42", "role": "admin", "exp": 1711321600}).
Signature: HMAC or RSA signature over the header and payload, proving the token wasn't tampered with.
The server verifies the signature using a secret key (HMAC) or public key (RSA/ECDSA). If the signature is valid and the token hasn't expired, the claims are trusted. No database lookup needed.
Advantages:
- Stateless — the server doesn't store tokens, so it scales without shared storage
- Self-contained — the token carries identity and permissions
- Cross-domain — sent in the
Authorizationheader, works across any origin - Works for APIs, mobile apps, and microservices
Limitations:
- Cannot be revoked before expiration (unless you maintain a blocklist, which defeats the stateless benefit)
- Payload is readable by anyone (base64, not encrypted) — don't put secrets in it
- Larger than session IDs (hundreds of bytes vs ~32 bytes)
- Short expiration times require refresh token flows
OAuth 2.0 — Delegated Authorization
OAuth 2.0 is not authentication — it's authorization delegation. It lets a user grant a third-party application limited access to their resources on another service, without sharing their password.
Example: "Sign in with Google" on a third-party app. You don't give the app your Google password. Instead, Google authenticates you and gives the app a token with limited permissions.
Authorization Code Flow
The most secure OAuth flow, used by web applications:
- User clicks "Sign in with Google" on the app
- App redirects the user to Google's authorization server
- User logs into Google and grants the app permission (e.g., "read your email")
- Google redirects back to the app with an authorization code
- App's backend exchanges the code + its client secret for tokens (server-to-server, not visible to the user)
- Google returns an access token (short-lived) and a refresh token (long-lived)
- App uses the access token to make API calls on behalf of the user
The authorization code is single-use and short-lived. The client secret never leaves the server. The access token expires quickly (minutes to hours). The refresh token obtains new access tokens without re-prompting the user.
Client Credentials Flow
For server-to-server communication (no user involved), the app authenticates directly with its client ID and secret:
POST /oauth/token
grant_type=client_credentials
client_id=xxx
client_secret=yyy
Simpler than authorization code, but only works when there's no user context.
Which Approach to Use
| Approach | Best for | Stateful? | Revocable? |
|---|---|---|---|
| API key | Server-to-server, developer tools | Yes (key lookup) | Yes |
| Session | Traditional web apps, same-domain | Yes (session store) | Yes (instant) |
| JWT | APIs, mobile, microservices | No (stateless) | No (until expiry) |
| OAuth 2.0 | Third-party access, "Sign in with X" | Depends on token type | Yes (revoke grant) |
In practice, many systems combine approaches. OAuth issues a JWT as the access token. The JWT is verified statelessly for most requests. A blocklist catches revoked tokens that haven't expired yet.
Next Steps
- How Rate Limiting Works — protecting APIs from overuse after authentication.
- How TLS Works — encrypting the transport layer that carries all these tokens.
- How Hashing Works — the cryptography behind JWT signatures and password storage.