The Problem That Created OAuth
In 2006, Twitter developer Blaine Cook was building an API for third-party apps. The only option at the time was asking users for their Twitter password directly. Yelp, a major Twitter partner, ended up storing millions of user passwords just to make its integration work. When Yelp had a security incident, every one of those users' Twitter accounts was compromised too. Cook, along with engineers from Google and other companies, designed OAuth to fix this: let users grant limited access to an app without ever handing over their password. That fix became the foundation for the OAuth 2.0 flows developers rely on today.
Today, every "Sign in with Google/GitHub/Facebook" button runs on OAuth 2.0 authentication, and over 2 billion OAuth authorizations happen daily. If you're doing any kind of system design work involving third-party access or API platforms, understanding these flows isn't optional.
The Four OAuth2 Roles
OAuth2 Actors:
Resource Owner (User)
'I own my Google photos'
|
| 1. 'Allow PhotoEditor to access my photos?'
| 2. 'Yes, allow read-only access'
v
Authorization Server (Google)
'I verify the user and issue tokens'
|
| 3. Issues access_token + refresh_token
v
Client (PhotoEditor App)
'I want to access the user's photos'
|
| 4. GET /photos Authorization: Bearer <token>
v
Resource Server (Google Photos API)
'I serve the photos if the token is valid'Authorization Code Flow (Web Apps)
This is the flow most people mean when they say "OAuth," and it's the one to reach for whenever your app has a backend that can keep a secret.
Authorization Code Flow (Most Secure):
User Client (Backend) Auth Server Resource Server
| | | |
|--1. Click 'Login with Google'------->| |
| | | |
|<--2. Redirect to Google login--------| |
| | | |
|--3. User logs in + consents--------->| |
| | | |
|<--4. Redirect with auth CODE---------| |
| | | |
|--5. Send code to YOUR backend------->| |
| | | |
| |--6. Exchange code--->| |
| | + client_secret | |
| | | |
| |<--7. access_token----| |
| | + refresh_token | |
| | | |
| |--8. GET /photos -----|--Bearer token----->|
| | | |
| |<---------photos-----|----photos----------||
|<--9. Render photos------------------| |
Security: access_token NEVER reaches the browser.
The auth code is single-use and expires in 60 seconds.PKCE Flow (Mobile/SPAs)
The OAuth authorization code flow assumes the client can hold a secret. Mobile apps and single-page apps can't, since anyone can decompile a binary or read a bundled JS file and pull the secret straight out. PKCE closes that gap.
Authorization Code + PKCE (Proof Key for Code Exchange):
Problem: Mobile apps and SPAs cannot store a client_secret safely.
Anyone can decompile an app and extract the secret.
Solution: Replace static secret with dynamic proof.
Before redirect:
1. Generate random code_verifier (43-128 chars)
2. Hash it: code_challenge = SHA256(code_verifier)
3. Send code_challenge with auth request
After redirect (exchanging code for token):
4. Send original code_verifier
5. Auth server hashes it and compares with stored code_challenge
6. Match? Issue token. No match? Reject.
Why this works:
- Attacker intercepts auth code? Useless without code_verifier
- code_challenge is a hash, cannot reverse to get code_verifier
- Different code_verifier for every auth attempt
PKCE is now recommended for ALL OAuth2 flows (RFC 7636).When to Use Which Flow
Picking the right OAuth 2.0 flow comes down to one question: can this client keep a secret? The table below is a decent starting point, and it's worth revisiting alongside your broader low-level design decisions, since auth touches almost every service boundary.
OAuth2 Flow Selection Guide:
Your Application Flow Why
-------------------- --------------------- --------------------------
Web app with backend Authorization Code Backend stores secrets safely
SPA (React, Vue) Auth Code + PKCE No safe secret storage
Mobile app (iOS/Android) Auth Code + PKCE App binary can be decompiled
Server-to-server (APIs) Client Credentials No user involved
CLI tool Device Authorization No browser redirect possible
Microservice-to-service Client Credentials Service identity, not user
DEPRECATED (never use):
- Implicit Flow: token in URL fragment, easily leaked
- Resource Owner Password: app receives user's password directlyCommon OAuth2 Mistakes
Not validating the state parameter, which enables CSRF attacks. Always generate a random state, store it in session, and verify it on callback.
Using the Implicit flow, deprecated since 2019. It exposes tokens in URL fragments and browser history.
Requesting too many scopes. Ask for the minimum permissions needed, so "scope=read:email" rather than "scope=admin."
Skipping PKCE for SPAs. Authorization Code without PKCE for public clients is vulnerable to code interception.
Storing tokens in localStorage, which is vulnerable to XSS. Use HTTP-only secure cookies instead.
Not validating redirect_uri. Open redirect attacks can steal auth codes, so whitelist exact redirect URIs rather than pattern-matching them.
Most of these mistakes also show up at the API boundary layer, right where you'd be deciding between an API gateway, BFF, or another protocol pattern. Token validation and scope checks are usually best centralized there rather than duplicated in every service.
Interview Tip
In system design interviews, OAuth2 comes up when designing social login, third-party integrations, or API platforms. Draw the Authorization Code flow with all four actors (user, client, auth server, resource server). Emphasize that the access token never reaches the browser in the standard flow. Mention PKCE as the modern standard for mobile and SPAs. If you're designing an API platform, explain how you'd implement the Client Credentials flow for service-to-service auth. The phrase interviewers want to hear: "delegated authorization without credential sharing."
If you're building up to these interviews, it's worth pairing this with broader prep resources, like this roundup of system design learning platforms, so OAuth doesn't end up as an isolated fact you memorized rather than a pattern you actually understand.
Key Takeaway
OAuth2 enables delegated authorization: users grant limited permissions to apps without sharing passwords. Use the Authorization Code flow for web apps, add PKCE for SPAs and mobile, and use Client Credentials for machine-to-machine calls. Never use the Implicit flow. Always validate state parameters and restrict redirect URIs. These are the OAuth 2.0 best practices that separate a secure implementation from one waiting to be exploited.
