HTTP Cheatsheet

Authentication

Use this HTTP reference while you build software engineering projects, review code, or refresh the syntax you reach for most.

The Authorization header and its schemes

Authentication rides on one request header and one challenge header.

GET /v1/me HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI4ODE0In0.xxxxx

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="api", error="invalid_token",
                  error_description="expired"
SchemeFormNotes
Basicbase64(user:pass)not encryption, trivially reversible, HTTPS only
Beareran opaque or JWT tokenRFC 6750, the API default
Digestchallenge-response hashavoids sending the password, obsolete in practice
NegotiateSPNEGO, Kerberos or NTLMWindows intranets
HOBAsignature basedrare
AWS4-HMAC-SHA256request signaturevendor scheme, signs method, path, headers, body hash

401 means "authenticate", and it must carry WWW-Authenticate. 403 means "authenticated, still not allowed", and retrying with the same credentials is pointless.

Basic auth

curl -u alice:s3cret https://api.example.com/v1/me
# equivalent to
printf 'alice:s3cret' | base64        # YWxpY2U6czNjcmV0
curl -H 'Authorization: Basic YWxpY2U6czNjcmV0' https://api.example.com/v1/me

Base64 is encoding, not hashing, so credentials travel in effectively plaintext. It is acceptable only over TLS, for machine-to-machine calls, or behind an internal proxy. Browsers cache the credentials for the realm and there is no clean logout, which is the practical reason it disappeared from user-facing apps.

Bearer tokens: opaque vs JWT

Opaque tokenJWT
Contenta random identifiersigned, self-describing claims
Validationa lookup, DB or cacheverify a signature locally
Revocationdelete the row, instanthard, needs a denylist or short TTLs
Sizesmall, 32-64 bytes500 bytes to several KB
Readable by the clientnoyes, base64 is not encryption
Statelessnessnoyes, which is the whole point
IntrospectionPOST /introspect (RFC 7662)decode locally
JWT structure, three base64url parts joined by dots

eyJhbGciOiJSUzI1NiIsImtpZCI6ImsxIn0    header  {"alg":"RS256","kid":"k1"}
.eyJzdWIiOiI4ODE0IiwiZXhwIjoxNzg5fQ    payload {"sub":"8814","exp":1789...}
.QhTsx9K7...                           signature over header.payload
ClaimMeaningVerify it
ississuermatches the expected issuer exactly
subsubject, the user id—
audaudienceyour API is in the list
expexpiry, epoch secondsnow < exp, allow small clock skew
nbfnot beforenow ≥ nbf
iatissued at—
jtiunique token idfor replay detection and denylists

JWT validation rules that get skipped and cause real breaches: never trust the alg in the header (pin the expected algorithm, or alg: none and HS256-with-the-public-key attacks apply), always verify the signature before reading any claim, always check exp and aud, and resolve kid against a JWKS you fetched from the issuer over TLS.

Never put secrets in a JWT payload. Anyone holding the token can read it.

OAuth 2.1 authorization code flow with PKCE

This is the flow to use for browsers, mobile apps, and SPAs. The implicit flow and the password grant are both deprecated.

1. Client makes a verifier and a challenge
   verifier  = 43-128 random chars
   challenge = base64url(sha256(verifier))

2. Redirect the user to the authorization server
   GET /authorize?response_type=code
       &client_id=abc
       &redirect_uri=https://app.example.com/cb
       &scope=openid%20profile%20orders.read
       &state=xyz789
       &code_challenge=E9Mel...&code_challenge_method=S256

3. User authenticates and consents, then comes back
   GET /cb?code=SplxlOB&state=xyz789      ← verify state matches

4. Exchange the code, sending the verifier
   POST /token
   grant_type=authorization_code&code=SplxlOB
   &redirect_uri=https://app.example.com/cb
   &client_id=abc&code_verifier=<the original verifier>

5. Response
   { "access_token":"...", "token_type":"Bearer", "expires_in":3600,
     "refresh_token":"...", "id_token":"..." }

state defends against CSRF on the callback. PKCE defends against code interception, because a stolen code is useless without the verifier. nonce in OpenID Connect defends against ID token replay.

GrantUse forStatus
Authorization code + PKCEweb apps, SPAs, mobile, CLIsthe default
Client credentialsservice to service, no userfine
Device codeTVs, CLIs with no browserfine
Refresh tokenrenewing access tokensfine, rotate them
Implicit—removed in OAuth 2.1
Resource owner password—removed in OAuth 2.1

OAuth is authorization. OpenID Connect adds authentication on top, in the form of an id_token (a JWT about the user) plus the /userinfo endpoint.

Sessions vs tokens

Server session cookieBearer token in JS
Storageserver side, cookie holds only an idlocalStorage, memory, or a cookie
XSS exposureprotected by HttpOnlyreadable by any injected script
CSRF exposureyes, needs SameSite plus a tokenno, if sent in a header
Revocationdelete the session, instantneeds short TTLs or a denylist
Cross-domain APIsawkward, CORS plus SameSite=Nonestraightforward
Scalingshared store, or sticky sessionsstateless

The pattern that gets the best of both for a browser app: keep the access token in memory only, keep the refresh token in an HttpOnly, Secure, SameSite=Strict cookie scoped to the refresh endpoint. Never put a long-lived token in localStorage, because one XSS is total account compromise with no expiry.

API keys, HMAC signing, and mTLS

# API key, simplest machine credential. header, never a query string
curl -H 'Authorization: Bearer sk_live_...' https://api.example.com/v1/orders
curl -H 'X-API-Key: sk_live_...' https://api.example.com/v1/orders

# HMAC request signing, resists replay and proves body integrity
ts=$(date +%s)
body='{"sku":"HU-100"}'
sig=$(printf '%s\n%s\n%s' "$ts" "POST /v1/orders" "$body" \
      | openssl dgst -sha256 -hmac "$SECRET" -binary | base64)
curl -X POST https://api.example.com/v1/orders \
  -H "X-Timestamp: $ts" -H "X-Signature: $sig" -d "$body"

# mutual TLS, the client proves itself with a certificate
curl --cert client.pem --key client.key https://api.example.com/v1/orders

Webhook receivers should verify an HMAC signature over the raw body, compare with a constant-time comparison, and reject timestamps outside a few minutes to stop replay.

Hardening checklist and failure modes

PracticeWhy
TLS everywhere, HSTS onevery scheme above leaks in plaintext
Short access token TTL (5-15 min)limits the blast radius of a leak
Rotate refresh tokens, detect reusea replayed refresh token means theft
HttpOnly, Secure, SameSite on session cookiesXSS and CSRF
Rotate the session id on login and privilege changesession fixation
Rate limit auth endpoints, return 429 with Retry-Aftercredential stuffing
Constant-time comparison for tokens and signaturestiming side channels
Hash passwords with argon2id or bcryptnever store or encrypt them
Keep tokens out of URLs, logs, and Refererquery strings leak everywhere
Scope tokens narrowly, one audience eacha stolen token should not be universal
# what does the server actually challenge with?
curl -sI https://api.example.com/v1/me | grep -i www-authenticate

# decode a JWT payload locally, no verification, inspection only
cut -d. -f2 <<<"$JWT" | tr '_-' '/+' | base64 -d 2>/dev/null | python3 -m json.tool