Authentication and authorization

Topics in this section
2 cards

Who is calling: keys and tokens

Authentication answers 'who is calling?'; authorization answers 'may they do this?'. Two common ways to authenticate an API call:

API keys identify an application or account: a long random secret, sent in a header, used for server-to-server calls. Bearer tokens are short-lived credentials, usually issued through OAuth 2.0, sent as Authorization: Bearer <token>. They identify a user (or a service acting for one), expire after minutes or hours, and can be revoked.

Rules for both: only over HTTPS; in a header, never in the URL (URLs end up in logs, browser history and Referer headers); store only a hash of API keys on the server, like passwords; and allow keys to be rotated without downtime.

In real life

Stripe uses secret API keys for server calls; GitHub accepts personal access tokens and OAuth tokens in the Authorization header and lets users revoke them individually.

In code: HTTP
GET /v1/orders
Authorization: Bearer eyJhbGciOiJSUzI1NiJ9…
→ 200 OK

GET /v1/orders                     # no credentials
→ 401 Unauthorized
WWW-Authenticate: Bearer

GET /v1/orders?api_key=sk_live_…   # don't: the key leaks into logs
  • 401 must include a WWW-Authenticate header saying which scheme to use (RFC 9110).
  • Bearer means whoever holds the token can use it, which is why HTTPS and short lifetimes matter.
Check yourself

Which practices are right for API credentials? (Choose three.)

Select 3, then check.

What they may do: scopes and ownership

Authorization happens on every request, for the specific resource. Two layers are common.

Scopes limit what a token may do at all: a token with orders:read can list orders but not refund them. Give each client the smallest set of scopes it needs. Ownership checks limit which records: user 7's token may read /orders/42 only if order 42 belongs to user 7.

Forgetting the ownership check is one of the most common API vulnerabilities (OWASP calls it broken object level authorization): change the id in the URL and you read someone else's data. The fix is to check every object access against the caller, not just 'is the user logged in'.

In real life

A support tool needs to read orders but never refund them: it gets a token with orders:read only. If it's compromised, the attacker can't move money.

Check yourself

User 7 is logged in and calls GET /invoices/9001, which belongs to user 8. Which statements are true? (Choose two.)

Select 2, then check.