OAuth 2.0 Flows Explained — Authorization Code, PKCE, Client Credentials
Use Authorization Code + PKCE for web apps and mobile apps where the user logs in. Use Client Credentials for server-to-server API calls with no user. Never use Implicit flow — it is deprecated. PKCE is required for any public client (SPA, mobile app) that cannot securely store a client secret.
OAuth 2.0 is not one protocol — it is four different flows (grant types) for four different situations. Picking the wrong flow causes security issues. This guide explains each flow, when to use it, and what it looks like in practice.
Diagnose your OAuth error → OAuthFixerFlow 1 — Authorization Code with PKCE (modern standard)
Use for: SPAs, mobile apps, any public client. This is the correct flow for most applications.
1. App generates code_verifier (random string) and code_challenge (SHA256 hash)
2. Redirect user to auth server:
GET /authorize?
response_type=code
&client_id=YOUR_CLIENT_ID
&redirect_uri=https://app.example.com/callback
&scope=openid profile email
&code_challenge=BASE64URL(SHA256(code_verifier))
&code_challenge_method=S256
3. User logs in, auth server redirects back:
https://app.example.com/callback?code=AUTH_CODE
4. App exchanges code for tokens:
POST /token
grant_type=authorization_code
&code=AUTH_CODE
&redirect_uri=https://app.example.com/callback
&client_id=YOUR_CLIENT_ID
&code_verifier=ORIGINAL_CODE_VERIFIER
5. Auth server returns access_token and refresh_token
Flow 2 — Authorization Code (confidential clients only)
Use for: server-side apps where client_secret can be stored securely (not in browser or mobile app).
Same as PKCE flow but uses client_secret instead of PKCE challenge. Never use this in a SPA or mobile app — the secret would be exposed.
Flow 3 — Client Credentials
Use for: machine-to-machine API calls. No user is involved.
POST /token
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
&client_id=YOUR_CLIENT_ID
&client_secret=YOUR_CLIENT_SECRET
&scope=api:read api:write
Response:
{
"access_token": "eyJ...",
"token_type": "Bearer",
"expires_in": 3600
}
# Use the token:
GET /api/data
Authorization: Bearer eyJ...
Flow 4 — Implicit (deprecated — do not use)
Returns the access token directly in the URL fragment. Deprecated in OAuth 2.1. Never use in new applications — tokens in URLs are logged by servers, proxies, and browser history.
Which flow to use
| Situation | Flow |
|---|---|
| SPA (React, Vue, Angular) | Authorization Code + PKCE |
| Mobile app (iOS, Android) | Authorization Code + PKCE |
| Server-side web app | Authorization Code (+ client_secret) |
| Backend API to API | Client Credentials |
| CLI tool | Authorization Code + PKCE or Device Code |
| Any new application | Never Implicit |
Common errors and causes
| Error | Cause |
|---|---|
invalid_grant | Auth code expired, already used, or wrong redirect_uri |
redirect_uri_mismatch | redirect_uri doesn't exactly match registered URI |
invalid_client | Wrong client_id or client_secret |
pkce_required | Auth server requires PKCE but code_challenge was not sent |