OAuth 2.0 Flows: Visualizing The Difference
Cover photo by Dan Nelson on Unsplash
OAuth 2.0 is just a series of redirects and token exchanges, but the terminology makes it sound like magic. You shouldn’t need a PhD to get it. Here is how the two most common flows actually look when you strip away the noise.
Authorization Code Flow
This is the gold standard for web apps. You have a server that can keep a secret. The browser sends the user to the provider, the provider sends a temporary code back to your callback URL, and then your server swaps that code for an access token. The secret never touches the browser.
Browser -> Auth Server (User logs in)Browser <- Auth Server (Code sent to callback)Server (Backend) -> Auth Server (Swap code + client_secret for token)Server (Backend) <- Auth Server (Access Token)Authorization Code Flow with PKCE
Maybe you’re building a Single Page App (SPA) or a mobile app. You don’t have a secure backend to hide your client secret. PKCE solves this by generating a random string (the code verifier) before the request, then sending a hash of it (the code challenge). If someone intercepts the code, they still can’t trade it without that initial secret. It’s essentially “Client Secret-less” authentication.
Client (Frontend) -> Generates Code Verifier & ChallengeClient (Frontend) -> Redirect with ChallengeClient (Frontend) <- Auth Server (Authorization Code)Client (Frontend) -> Swap Code + Original Verifier for TokenWhy the difference matters
“But why can’t I just use the same flow for everything?”
That is a fair question, and honestly, if you have a backend, you should use the standard Authorization Code flow every single time. It’s safer. The PKCE flow is only for environments where you literally cannot keep a secret hidden from the user. (I’ll admit, sometimes it feels like overkill to add PKCE for simple internal tools, but if you’re deploying to production, just turn it on.)
The trade-offs
Use the standard Authorization Code flow if you have a Node, Python, or Go backend. It keeps your tokens out of the browser’s reach, which is where most attacks happen (I’m looking at you, XSS).
Use PKCE if you are building an SPA or a native mobile app. It’s the only secure way to handle authentication without a backend proxy. Don’t fall for the “Implicit Flow” trap. That one is deprecated for a reason.
I prefer keeping things simple. If your app has a server, treat it like an asset and keep your secrets there. If you’re building a pure client-side application, stick to PKCE and assume the browser is a hostile environment. Does this cover every edge case? No. Some providers handle Refresh Tokens differently, and that’s a whole other headache for another day.
Which flow does your current project use, and have you ever had to deal with the legacy mess of the Implicit flow?