security · intermediate

OAuth 2.0, OIDC, and JWT — Delegation Without Sharing Passwords

Start here

Three related pieces that people often mash together:

  1. OAuth 2.0 — authorization framework: an app gets limited access to a user’s resources on another system without the user’s password.
  2. OpenID Connect (OIDC) — identity layer on OAuth: the app learns who logged in (ID token).
  3. JWT (JSON Web Token) — a compact signed token format often used for access/ID tokens.
You should care because almost every modern SaaS login and “Login with Google/GitHub” flow uses these ideas—and most vulnerabilities come from **wrong flow choice**, **open redirects**, **token leakage**, or **treating JWT as encrypted when it is only signed**.

What you will learn

  1. Separate authentication vs authorization language.
  2. Map roles: resource owner, client, authorization server, resource server.
  3. Choose authorization code + PKCE for public clients.
  4. Understand access tokens vs refresh tokens vs ID tokens.
  5. Validate JWTs correctly.
  6. Avoid common anti-patterns.

Words you need before we begin

TermPlain English
Resource ownerUsually the end user.
ClientApp requesting access (SPA, mobile, backend).
Authorization server (AS)Issues tokens after login/consent (IdP).
Resource server (RS)API that accepts access tokens.
Access tokenCredential for calling APIs.
Refresh tokenLong-lived credential to get new access tokens.
ID tokenOIDC token asserting user identity to the client.
PKCEProof Key for Code Exchange—protects public clients.
ScopeLimited permissions requested/granted.
JWTSigned (optionally encrypted) JSON token structure.

Simple story: hotel key card

You (resource owner) check in at the front desk (authorization server). You get a key card (access token) that opens gym and room (scopes), not the vault. You do not give the gym your passport every time—only the card. OIDC is the desk also giving your app a badge that says your name (ID token).

The problem OAuth solves

Before OAuth, apps asked for your password to another site (“give us your Gmail password to import contacts”). That is catastrophic: apps see passwords; revocation is hard; least privilege is impossible.

OAuth lets you delegate with scopes and revocable tokens.

Step-by-step: authorization code + PKCE (modern default)

Step 1 — Client prepares PKCE

Generate code_verifier; derive code_challenge.

Step 2 — Redirect user to AS

Browser goes to authorize URL with client_id, redirect_uri, scope, state, code_challenge.

Step 3 — User authenticates and consents

AS authenticates user; user approves scopes.

Step 4 — Redirect back with code

Only if redirect_uri exactly matches allowlist. Validate state (CSRF).

Step 5 — Client exchanges code for tokens

Back-channel (or app) POST to token endpoint with code + code_verifier. Receives access token (+ refresh, + id_token for OIDC).

Step 6 — Call APIs with access token

Authorization: Bearer … to resource server. RS validates token (JWT locally or introspection).

Step 7 — Refresh carefully

Store refresh tokens securely (httpOnly secure cookies or secure mobile storage—not localStorage if avoidable). Rotate when supported.

JWT validation essentials

For signed JWT access/ID tokens:

  1. Validate signature with the right keys (JWKS).
  2. Validate iss (issuer) and aud (audience).
  3. Validate exp (expiry) with small clock skew.
  4. Do not trust unsigned alg=none.
  5. Remember JWT payload is readable if only signed—not secret.

Visual mental model

sequenceDiagram
  participant U as User
  participant C as Client app
  participant AS as Auth server
  participant API as Resource API
  U->>C: Login
  C->>AS: redirect authorize + PKCE
  U->>AS: authenticate + consent
  AS->>C: redirect code
  C->>AS: token exchange
  AS-->>C: access + id tokens
  C->>API: API call + access token
  API-->>C: data

Learning question: Why is the authorization code redirected to the browser instead of sending tokens in the first redirect for public clients?

Caption: Code + PKCE reduces token exposure; tokens obtained via direct token request with verifier.

Complete worked example: “Login with IdP” for a SaaS API

SPA or mobile app uses AS. After login, SPA calls api.myapp.com with access token scoped orders:read. API validates JWT aud=api.myapp.com, iss=https://idp.example/, signature via JWKS, and enforces user permissions server-side (token is not the only authz).

Failure modes / attacks (intro)

IssueRiskMitigation
Open redirect_uriToken/code theftExact allowlist
Missing stateCSRF on loginRequired state
Tokens in URL fragments loggedLeakagePrefer code flow; careful storage
Accepting any JWT signing keyForgeryPin iss/jwks carefully
Long-lived access tokens in LSXSS theftShort TTL; better storage
Confused deputyWrong audienceValidate aud

Trade-offs

ChoiceBenefitCost
Opaque tokens + introspectionRevocation easyAS dependency on each call
JWT access tokensLocal validateRevocation harder
Session cookies for first partySimple CSRF modelCross-site patterns differ
OAuth for first-party login onlySometimes overkillComplexity

Common misunderstandings

  1. “OAuth is authentication.” OAuth is authorization; OIDC adds authN.
  2. “JWT is always encrypted.” Often only signed.
  3. “Put roles only in JWT forever.” Authorization can change; keep TTLs short or check server.
  4. “Implicit flow is fine.” Considered obsolete for SPAs—use code+PKCE.
  5. “HTTPS optional in dev excuses production mistakes.” Redirect URIs and cookies need real hygiene.

Check your understanding

  1. Name the four OAuth roles.
  2. What problem does PKCE solve?
  3. Access token vs ID token?
  4. List three JWT checks.
  5. Why allowlist redirect URIs?

Practice

  1. Draw code+PKCE for a mobile app.
  2. Write pseudocode for API JWT middleware.
  3. Compare first-party session login vs OIDC.
  4. Draft scopes for a photo app.
  5. Find one historical OAuth vulnerability class and map to a row above.

Deeper production notes

Service-to-service

Client credentials flow for machine clients—no user—still needs secret management and least privilege scopes.

Key rotation

JWKS rotation must be supported; cache keys with kid.

Specialist boundary

Standards and browser rules evolve (BCP for browser apps). Re-read current OAuth/OIDC BCPs before production freeze.

Additional teaching scenarios

Scenario A — 10× peak

What breaks first? Mitigation?

Scenario B — dependency outage

What still works?

Scenario C — teach-back

Five sentences: problem, mechanism, example, failure, trade-off.

Revision summary

Glossary

TermDefinition
OAuth 2.0Delegation framework for limited access.
OIDCIdentity layer on OAuth.
JWTJSON-based token format, often signed.
PKCEExtension protecting auth code flows for public clients.

Abbreviations and terminology

What to learn next

  1. Session vs JWT
  2. TLS, mTLS, PKI
  3. Spring Security OAuth2/JWT

Extra teaching notes

When you apply this lesson, write the user-visible success metric first, then the failure mode you fear most. Design the smallest mechanism that protects that metric under partial failure. Prefer explicit timeouts, idempotency, and ownership over adding more infrastructure boxes.

In interviews or design reviews, narrate assumptions, request paths, and trade-offs out loud. A correct-enough design with clear failure handling beats a buzzword diagram without numbers. Revisit the worked example and restate it for a different domain to prove you own the ideas, not the template wording.

Extra teaching notes

When you apply this lesson, write the user-visible success metric first, then the failure mode you fear most. Design the smallest mechanism that protects that metric under partial failure. Prefer explicit timeouts, idempotency, and ownership over adding more infrastructure boxes.

In interviews or design reviews, narrate assumptions, request paths, and trade-offs out loud. A correct-enough design with clear failure handling beats a buzzword diagram without numbers. Revisit the worked example and restate it for a different domain to prove you own the ideas, not the template wording.

Extra teaching notes

When you apply this lesson, write the user-visible success metric first, then the failure mode you fear most. Design the smallest mechanism that protects that metric under partial failure. Prefer explicit timeouts, idempotency, and ownership over adding more infrastructure boxes.

In interviews or design reviews, narrate assumptions, request paths, and trade-offs out loud. A correct-enough design with clear failure handling beats a buzzword diagram without numbers. Revisit the worked example and restate it for a different domain to prove you own the ideas, not the template wording.

Extra teaching notes

When you apply this lesson, write the user-visible success metric first, then the failure mode you fear most. Design the smallest mechanism that protects that metric under partial failure. Prefer explicit timeouts, idempotency, and ownership over adding more infrastructure boxes.

In interviews or design reviews, narrate assumptions, request paths, and trade-offs out loud. A correct-enough design with clear failure handling beats a buzzword diagram without numbers. Revisit the worked example and restate it for a different domain to prove you own the ideas, not the template wording.

Extra teaching notes

When you apply this lesson, write the user-visible success metric first, then the failure mode you fear most. Design the smallest mechanism that protects that metric under partial failure. Prefer explicit timeouts, idempotency, and ownership over adding more infrastructure boxes.

In interviews or design reviews, narrate assumptions, request paths, and trade-offs out loud. A correct-enough design with clear failure handling beats a buzzword diagram without numbers. Revisit the worked example and restate it for a different domain to prove you own the ideas, not the template wording.

Interview and production field guide for this topic

Use this section as deliberate practice, not filler. Rewrite the worked example for a second domain you know well—fintech, education, logistics, or media. Keep the same skeleton: requirements, estimates, high-level diagram, request path, data model, scale lever, failure modes, and trade-offs. If you cannot fill every section without copying buzzwords, you do not yet own the design.

Numbers to force yourself to state

Always speak order-of-magnitude figures: peak QPS, storage growth per day, fan-out factor, connection counts, or queue depth. Wrong numbers that are explicit beat silent hand-waving. Correct the numbers when the interviewer or teammate challenges them; that is collaboration, not failure.

Failure minute

Set a timer for sixty seconds and list only failures: timeouts, duplicates, hot keys, dependency outages, bad deploys, and data corruption paths. For each, name detection and first mitigation. Designs that only describe the happy path are incomplete for production and weak in interviews.

Ownership and operability

Name the dashboard, the alert, the runbook section, and the team that pages. If any are blank, the system will train you during an incident. Prefer progressive delivery: canaries, flags, and rollback notes written before the change lands.

Consistency and retries

State whether the design assumes at-least-once delivery, whether handlers are idempotent, and where unique constraints live. Retries without idempotency are how double charges, double messages, and duplicate fan-out jobs appear. Timeouts without bounds are how thread pools die.

What good looks like in a review

A strong design review or interview answer clarifies scope, makes assumptions audible, draws a minimal path, deepens one or two bottlenecks, and closes with trade-offs and evolution. Use that bar on oauth2-oidc-jwt-deep-dive round 0 every time you revisit it.

FAQ from first-time learners

Q: Is Auth0/Cognito required?
A: No—concepts are the same whether you build or buy the AS.

Q: Can access tokens be JWTs?
A: Yes, commonly; opaque tokens are also valid.

Track: Security and Identity

Previous: Threat Modelling for Backend Services

Next: Secrets and Key Management

By Shubham Jain

All articles · Study paths

Shubham Jain · Learning Lab