Most OAuth bugs are design mistakes — made before the first line of code is written
A two-question decision guide for picking the right flow, every time.
After 21+ years building and architecting software systems, one pattern keeps showing up in security reviews: OAuth is implemented incorrectly more often than correctly — and almost always because the wrong flow was chosen at the start.
Not because developers don’t understand OAuth. Because there are too many flows, most documentation assumes context you don’t have, and the wrong choice isn’t obvious until something breaks in production.
Here’s the decision guide I use. Two questions. That’s it.
Question 1: Is a user signing in?
→ No → Client Credentials flow. Your app authenticates as itself. No redirect, no user consent, no browser. Use this for service-to-service and machine-to-machine calls.
→ Yes → Go to Question 2.
Question 2: What type of application is calling the API?
→ Web app with a backend → Authorization Code flow. The client secret never leaves your server. The code exchange happens server-to-server.
→ SPA with no secret-holding backend → Authorization Code + PKCE. No long-lived secret required. This replaces the deprecated Implicit flow — don’t use Implicit.
→ Native mobile or desktop app → Authorization Code + PKCE. No embedded secrets in the app package. Redirects are handled by the OS.
→ CLI or headless device → Device Code flow. Users visit a short URL and enter a code while the device polls for authentication. Purpose-built for no-browser environments.
One thing most OAuth guides quietly skip:
Your API doesn’t choose the flow. It validates Bearer tokens (RFC 6750). The flow is chosen by whoever calls the API. This matters architecturally — it means your API is flow-agnostic by design.
The fundamental split behind all of this is simple: confidential clients (web apps with a backend) can store a secret securely. Public clients (SPAs, native apps, CLIs) cannot. PKCE exists precisely because of that gap — it prevents authorization code interception attacks without requiring a stored secret.
OAuth isn’t conceptually hard. It gets complicated when the wrong flow is chosen upfront. Run the two questions before writing a line of auth code and you’ll eliminate most of the bugs before they’re possible.



