Frontend Labs
⋯

64. HTTP authentication and authorization on the wire

64.1 The mechanisms

Scheme Wire format Notes
Basic Authorization: Basic base64(user:pass) Not encryption — base64 is reversible. HTTPS only. Fine for internal/service use.
Digest Challenge-response with nonces Avoids sending the password, but weak by modern standards and rarely used
Bearer Authorization: Bearer <token> The dominant API pattern (OAuth 2.0, RFC 6750)
API key Usually a custom header (X-Api-Key) or query param Query params leak into logs, Referer, and browser history — use a header
mTLS Client certificate at the TLS layer (Ch 38.10) Strongest; identity established before HTTP starts
Cookie session Cookie: (Ch 60) Browser-native; brings CSRF concerns
HMAC request signing Signature over method + path + body + timestamp (AWS SigV4 style) Resists replay and tampering; no bearer token to steal

64.2 The 401 challenge flow

Client:  GET /private
Server:  401 Unauthorized
         WWW-Authenticate: Bearer realm="api", error="invalid_token",
                           error_description="expired"
Client:  GET /private
         Authorization: Bearer eyJ...
Server:  200 OK

401 vs 403: 401 = "I don't know who you are (or your credentials are bad) — authenticate and retry." 403 = "I know who you are and you may not do this — retrying won't help." Sending 401 for an authorization failure causes clients to loop through token refresh pointlessly.

407 Proxy Authentication Required is the same flow for a proxy, using Proxy-Authenticate / Proxy-Authorization.

64.3 OAuth 2.0 / OIDC at the wire level

You don't need the whole spec, but you should recognise the shapes:

Authorization Code + PKCE  (the correct flow for web and mobile apps)
  1. browser -> /authorize?response_type=code&client_id=..&redirect_uri=..
                &scope=..&state=..&code_challenge=..&code_challenge_method=S256
  2. user authenticates at the identity provider
  3. redirect back with ?code=..&state=..        <- state defends against CSRF
  4. server-side: POST /token with code + code_verifier  -> access_token,
                                                            refresh_token, id_token
  5. call the API with  Authorization: Bearer <access_token>

Client Credentials  (machine-to-machine)
  POST /token  grant_type=client_credentials  -> access_token

Operational notes:

  • Access tokens should be short-lived (minutes); refresh tokens long-lived and rotated on use.
  • JWTs are not encrypted — they're signed. Anyone can read the payload (base64url decode it). Never put secrets in one.
  • Validate: signature, iss, aud, exp, nbf, and algorithm (reject alg: none and algorithm confusion).
  • Revocation is the JWT weakness — a stateless token is valid until it expires. Keep lifetimes short, or maintain a denylist.

64.4 Real-world example

An internal API used API keys passed as ?api_key=.... The keys turned up in: the CDN's access logs, the load balancer's logs, the Referer header sent to a third-party analytics script on an error page, and a screenshot in a support ticket. Rotating them took a week. Moving to Authorization: Bearer plus short-lived tokens removed four of those exposure paths immediately.

Try it yourself

curl -sI https://httpbin.org/basic-auth/user/pass | head -3      # 401 + WWW-Authenticate
curl -s  -u user:pass https://httpbin.org/basic-auth/user/pass   # 200

# decode a JWT payload (it is NOT encrypted)
echo '<jwt>' | cut -d. -f2 | tr '_-' '/+' | base64 -d 2>/dev/null | python3 -m json.tool

Generated from real-life-example.md · 8 Oct 2026 16:36