NOTE

Most of the content in this work was generated with the assistance of AI and carefully reviewed, edited, and curated by the author. If you have found any issues with the content on this page, please do not hesitate to contact me at support@issacc.com.

OAuth 2.0 — Deep Dive Cheat Sheet

A practical, systems-architect view of OAuth 2.0 (and OIDC): roles, flows, tokens, security patterns, and troubleshooting. Copy/paste ready snippets included.


1) Mental Model (What OAuth Is/Isn’t)

  • OAuth ≠ login. It’s delegation/authorization: let a client act on a user’s behalf with limited scopes and lifetimes.
  • OIDC adds authentication (identity) on top of OAuth via ID Tokens.
  • Trust boundaries: Authorization Server (AS) issues tokens; Resource Server (RS) validates tokens; Client holds tokens but should not learn user password.

2) Core Actors & Contracts

ActorResponsibilityNotes
Resource Owner (User)Grants consentSees consent screen for scopes
Client (App)Requests tokens, calls APIsPublic or confidential
Authorization Server (AS)Authenticates user, issues tokens/authorize, /token, JWKS
Resource Server (RS/API)Validates tokens, enforces scopesChecks iss, aud, exp, scope

Key Artifacts: access_token (short-lived), refresh_token (longer-lived), scope, client_id (+ client_secret for confidential clients).


3) Which Flow Should I Use?

flowchart LR
  A[What are you building?] -->|Browser SPA or Mobile| B[Auth Code + PKCE]
  A -->|Web app w/ backend| C[Auth Code]
  A -->|Machine-to-machine| D[Client Credentials]
  A -->|TV/Console/IoT| E[Device Code]
  B --> F[No client secret; use PKCE]
  C --> G[Keep secret on server only]
  D --> H[No user; service principal]
  E --> I[User types code on second device]

4) Authorization Code + PKCE Deep Walkthrough

sequenceDiagram
  participant U as User
  participant C as Client (SPA/Mobile/Web)
  participant AS as Authorization Server
  participant RS as Resource Server

  U->>C: Click Sign in with X
  C->>AS: GET /authorize (client_id, redirect_uri, response_type=code, scope, state, code_challenge)
  AS-->>U: Login + Consent
  U->>AS: Approve
  AS-->>C: 302 redirect (code, state)
  C->>AS: POST /token (code, code_verifier, client_id, redirect_uri)
  AS-->>C: access_token (+ refresh_token, id_token?)
  C->>RS: API call with Authorization: Bearer <access_token>
  RS-->>C: Protected data

PKCE: code_verifiercode_challenge = BASE64URL(SHA256(verifier)). Prevents intercepted codes from being exchanged by attackers.


5) Token Anatomy & Validation (JWT focus)

Access Token (JWT) — typical claims

{
  "iss": "https://auth.example.com/",
  "sub": "user-123",
  "aud": "api.example.com",
  "exp": 1731788575,
  "iat": 1731784975,
  "scope": "email profile.read"
}

RS validation checklist

  • Verify signature against AS JWKS.
  • Check exp (not expired), nbf (if present).
  • Check aud matches this API.
  • Check iss matches expected issuer.
  • Enforce scope to specific endpoints/operations.

Opaque tokens: RS calls AS /introspect → returns {active, scope, sub, aud, exp, ...}.


6) OIDC (Authentication on Top)

  • Adds ID Token (JWT) with identity claims (sub, email, name, etc.).
  • Adds nonce to mitigate token replay.
  • Endpoints: /.well-known/openid-configuration, userinfo.

Use OIDC whenever you need to know who the user is, not just access their data.


7) Security Playbook

  • HTTPS everywhere
  • PKCE for public clients (SPAs, mobile)
  • Short-lived access tokens (5–15 min)
  • Refresh token rotation (revoke previous on each use)
  • Least privilege scopes (ask for what you need only)
  • CSRF defense via state in /authorize (and nonce for OIDC)
  • Audience restriction: each API has its own audience; don’t reuse tokens across APIs
  • Key rotation: AS rotates signing keys; RS fetches via JWKS and caches
  • Secure storage: Prefer HTTP-only, SameSite cookies or in-memory storage for browser apps; avoid localStorage for long-lived secrets

8) Practical Snippets

8.1 Authorization Request (browser)

GET /authorize?response_type=code&client_id=CLIENT_ID&redirect_uri=https%3A%2F%2Fapp.example.com%2Fcb&scope=openid%20profile.read&state=xyz&code_challenge=abc&code_challenge_method=S256 HTTP/1.1
Host: auth.example.com

8.2 Token Exchange

curl -X POST https://auth.example.com/token \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d 'grant_type=authorization_code' \
  -d 'code=AUTH_CODE' \
  -d 'client_id=CLIENT_ID' \
  -d 'client_secret=CLIENT_SECRET' \
  -d 'redirect_uri=https://app.example.com/cb' \
  -d 'code_verifier=VERIFIER'

8.3 Calling the API

curl https://api.example.com/v1/me \
  -H 'Authorization: Bearer ACCESS_TOKEN'

8.4 Introspection (opaque tokens)

curl -u CLIENT_ID:CLIENT_SECRET \
  -d 'token=ACCESS_TOKEN' \
  https://auth.example.com/introspect

9) Common Errors & Fixes (Field Guide)

ErrorTypical CauseFix
invalid_grantBad/expired code; redirect_uri mismatch; no valid scopes granted; reused codeEnsure code is unexpired, single-use, redirect URI matches, scopes requested/approved
invalid_clientWrong client_id/secret, wrong auth methodVerify credentials and client auth method (basic vs body)
invalid_scopeScope string unknown or not allowed for clientRequest only allowed scopes; check server config
invalid_redirect_uriMismatch with registered URIsRegister exact redirect URI; use HTTPS
unauthorized_clientFlow not allowed for this clientEnable the grant type in AS client settings
invalid_token at APIExpired token; wrong audience/issuer; signature failValidate iss/aud/exp; refresh token; check JWKS keys

Salesforce/Google/etc.: If you see invalid_grant: no valid scopes defined, confirm the connected app scopes and that the user/org policy allows them; re-consent as needed.


10) Designing the Authorization Server (AS) — Internal View

  • Endpoints: /authorize, /token, /jwks.json, /introspect, /revoke, /userinfo (OIDC)
  • Client registration: static (console) or dynamic (RFC 7591)
  • Consent UX: scope grouping, incremental auth, remembered consent
  • Token service: issue JWT (sign with rotating keys), set lifetimes by flow & risk
  • Refresh rotation: on /token with refresh grant → issue new refresh, revoke old
  • Revocation: /revoke to invalidate stolen tokens
  • Risk signals: IP, device, geo, anomaly detection → force reauth or step-up MFA

11) Resource Server (API) Pattern

  • Fetch JWKS via /.well-known/openid-configurationjwks_uri
  • Cache keys; support rotation (kid switching)
  • Middleware validates: signature → expissaudscope
  • Map scopes → permissions at endpoint level
  • Prefer 401 for invalid/missing token; 403 for insufficient scope

Example pseudo-middleware

async function authz(req, res, next) {
  const token = parseBearer(req.headers.authorization);
  if(!token) return res.status(401).end();
  const claims = await verifyJWT(token, jwks);
  if(claims.iss !== ISS || !claims.aud.includes(API_AUD)) return res.status(401).end();
  if(isExpired(claims.exp)) return res.status(401).end();
  if(!hasScope(claims.scope, requiredScopeFor(req))) return res.status(403).end();
  req.user = { sub: claims.sub, scope: claims.scope };
  next();
}

12) Browser Storage & Cookies (SPA)

  • Prefer Authorization Code + PKCE.
  • Use in-memory storage for access tokens; refresh via backend or BFF (Backend-for-Frontend) pattern.
  • If using cookies: set HttpOnly, Secure, SameSite=Lax/Strict; avoid storing refresh tokens in JS-accessible storage.

13) Scope Design Guidelines

  • Keep scopes resource-oriented: calendar.read, calendar.write vs vague full_access.
  • Support incremental consent (start with read-only; request write later).
  • Avoid overbroad default scopes.

14) Threats & Mitigations

ThreatVectorMitigation
Code interceptionNetwork/MITMPKCE; HTTPS
CSRF on /authorizeCross-site linkstate (and nonce for OIDC)
Token theftXSS, log leakageHttpOnly cookies, minimal logs, short TTL
Audience confusionToken used across servicesEnforce aud per API
Refresh token replayStolen RTRotation + revoke previous
Open redirectMisconfigured redirect_uriExact allowlist with HTTPS

15) Quick Provider Template

Discovery: GET https://<issuer>/.well-known/openid-configuration Authorize: GET <issuer>/authorize Token: POST <issuer>/token JWKS: GET <issuer>/.well-known/jwks.json UserInfo (OIDC): GET <issuer>/userinfo


16) Debug Checklist (Copy/Paste)


17) Mini Glossary

  • AS: Authorization Server
  • RS: Resource Server (API)
  • PKCE: Proof Key for Code Exchange
  • JWKS: JSON Web Key Set (public keys for verifying JWTs)
  • BFF: Backend-For-Frontend pattern to keep tokens off the browser