OAuth2: one step at a time

Companion posts for oauth-lab on GitHub: incremental Flask snapshots from authorization code flow through JWKS, RS256, RFC 8707 resource indicators, and RFC 8693 token exchange (on-behalf-of). Read in order.

  1. v01 – Learning OAuth 2 by Building It, One Version at a Time
  2. v02 – Adding OAuth State to Stop CSRF
  3. v03 – Adding PKCE to Stop Authorization Code Interception
  4. v04 – Using the Access Token on a Protected API
  5. v05 – Refresh Tokens and Silent Re-authentication
  6. v06 – Splitting the Auth Server from the Resource Server
  7. v06b – JWT Access Tokens: Local Validation Without the Introspection Hop
  8. v07 – Adding OpenID Connect on Top of OAuth 2
  9. Intermission – What Industry Ships and Who Gets Paid
  10. v08 – JWKS and RS256: Dropping the Shared JWT Secret
  11. v09 – Resource Indicators: Binding Tokens to a Specific API
  12. v10 – Token Exchange (On-Behalf-Of): Swapping Tokens for Downstream APIs

A note on usage of LLMs #

These posts and the oauth-lab repo were written while I was learning OAuth and OIDC myself. I used LLMs heavily: boilerplate and styling in the Flask apps, RFC lookups, diagram and table formatting, prose cleanup, and (in the intermission post) market research and reference gathering.

The protocol flow, security tradeoffs, and what each version adds are things I built and debugged by hand. When something in a post reads too polished or too generic, that is probably the model; when it describes a bug I hit or a /debug/state dump that looked wrong, that is me.

Treat vendor numbers and analyst figures as snapshots, not gospel. Verify anything you plan to rely on against primary sources. If you spot an error, open an issue or tell me.

History of changes #

2026.07.11: Split JWT local validation out of v06 into a companion post (v06b). v06 was doing too much at once: server split plus both introspection and JWT Mode B; and those JWT claims resurface properly in v08 with JWKS anyway. Also added a few forward links now that later posts are live.