Updated April 2026

OAuth 2.0 Flows Explained — Authorization Code, PKCE, Client Credentials

Quick Answer

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 → OAuthFixer

Flow 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

SituationFlow
SPA (React, Vue, Angular)Authorization Code + PKCE
Mobile app (iOS, Android)Authorization Code + PKCE
Server-side web appAuthorization Code (+ client_secret)
Backend API to APIClient Credentials
CLI toolAuthorization Code + PKCE or Device Code
Any new applicationNever Implicit

Common errors and causes

ErrorCause
invalid_grantAuth code expired, already used, or wrong redirect_uri
redirect_uri_mismatchredirect_uri doesn't exactly match registered URI
invalid_clientWrong client_id or client_secret
pkce_requiredAuth server requires PKCE but code_challenge was not sent
Diagnose your OAuth error → OAuthFixer