Auth is two questions: who are you (authentication) and what can you do (authorisation). Conflating them, or using the wrong mechanism, is one of the most expensive mistakes in API design.
API Keys
The simplest mechanism: a long random string the caller includes on every request, usually in a header.
GET /v1/orders
Authorization: Bearer sk_live_a1b2c3...
- + Trivial to implement and use.
- + Great for server-to-server.
- − Long-lived; if leaked, full access until rotated.
- − No granular scopes by default — add scopes per key.
Best practices: prefix keys (sk_live_, pk_test_); store hashed in DB; show plaintext only at creation; provide rotation; rate-limit per key.
OAuth 2.0
OAuth solves a different problem: a user grants a third-party app limited access to resources they own, without sharing their password. The app exchanges a code for an access token; the token authorises subsequent API calls.
Common flows
| Flow | For |
|---|---|
| Authorization Code + PKCE | Web and mobile apps acting on behalf of a user (the modern default) |
| Client Credentials | Server-to-server, no user involved |
| Device Code | TVs, CLIs, IoT — devices with no browser |
| Implicit | Deprecated — do not use |
| Resource Owner Password | Deprecated — do not use |
Scopes
An access token carries scopes — fine-grained permissions:
scope: orders:read invoices:read
scope: orders:read orders:write
The API checks the scope on the token before allowing the operation. Use small scopes; let users see what they grant.
OpenID Connect (OIDC)
OAuth grants access to APIs. OIDC adds an identity layer: the IdP returns an ID token (a JWT) that proves who the user is. Use OIDC when your API needs to know the user, not just that some app is authorised.
JWT (JSON Web Tokens)
A JWT is a self-contained, signed token:
eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ1c2VyXzEiLCJleHAiOjE2OTc...
Three parts (header, payload, signature). The API verifies the signature with the issuer's public key and trusts the claims:
{
"iss": "https://auth.example.com",
"sub": "user_42",
"aud": "api.example.com",
"exp": 1737460000,
"iat": 1737456400,
"scope": "orders:read"
}
Claims to validate, every time
iss— issuer matches what you trust.aud— audience matches your API.exp— not expired.nbf— not used before its activation time.- Signature verified against issuer's public key (JWKS endpoint).
JWT pitfalls
- Long lifetimes turn JWTs into leaked-key disasters. Keep access tokens short (5–15 min); use refresh tokens to get new ones.
- JWTs cannot be revoked easily — use a short TTL or a denylist.
- Never accept
alg: none. Always pin algorithm. - Don't store sensitive data in claims — JWTs are signed, not encrypted.
mTLS
For service-to-service auth in zero-trust networks, mutual TLS proves both peers via certificates. Service meshes (Istio, Linkerd) automate this. No tokens to leak, but operational overhead is real.
Authorisation: Beyond Authentication
Knowing who the caller is doesn't tell you what they can do. Authorisation models:
| Model | Granularity | Use |
|---|---|---|
| RBAC | Roles → permissions | Internal apps, admin panels |
| ABAC | Attributes (user, resource, context) | Complex policies, multi-tenant |
| ReBAC | Relationships (Zanzibar-style) | Sharing models, social graphs |
| Scopes | Coarse permission tags on tokens | Public APIs, third-party apps |
Tooling: Open Policy Agent (OPA), Cedar (AWS), SpiceDB / Authzed, Oso. For most APIs, start with simple RBAC and add ABAC where needed.
Defence in Depth
Don't rely on one layer. Layer multiple checks:
- Edge / gateway — token validity, rate limits, basic shape.
- Service — business rules, scope checks.
- Data layer — row-level security, tenant isolation.
If one layer is bypassed, the others still hold.
Security Hygiene
- HTTPS everywhere; reject HTTP.
- Rotate keys regularly; provide rotation primitives.
- Never log full tokens or keys.
- Hash keys at rest; show plaintext once.
- CSRF tokens for browser-driven cookies; not needed for bearer tokens.
- CORS only as permissive as needed.
Cert Mapping
| Cert | Auth scope |
|---|---|
| AWS SAA / SCS | Cognito, IAM auth on API Gateway, Lambda authorizers |
| AWS Data Engineer | IAM-signed API requests, secret rotation |
The next lesson covers the runtime concerns that protect a public API from abuse: rate limiting and caching.