OAuth 2.0 Flows Explained
OAuth 2.0 delegates access. A user grants an app a limited token to act on their behalf, without sharing the password. Different flows for different client types.
1 credit
The flows
6 itemsAuthorization Code + PKCE
The right default. SPAs, mobile, server apps. User-agent redirect + one-time code exchanged for token.Authorization Code (classic)
Server apps with a client secret. Same as above, no PKCE. Use PKCE version for new builds.Client Credentials
Server-to-server, no user involved. Your backend → vendor API.Device Code
Input-constrained devices (TV, CLI). User opens a URL on their phone, enters a code.Implicit (deprecated)
Token in URL fragment. Don't use. Superseded by Auth Code + PKCE.Password (deprecated)
User types pwd into 3rd-party app. Negates the whole point of OAuth.Token types
- **Access token** — short-lived (5-60 min), sent with each API call (`Authorization: Bearer <token>`).
- **Refresh token** — long-lived, stored securely, exchanged for new access tokens. Rotate on every use when possible.
- **ID token (OIDC)** — JWT about the user (who they are). OAuth alone doesn't prove identity; OpenID Connect layers that on top.
Scopes
- Request the minimum needed — `read:profile` beats `full_access`.
- Show the user what they're granting before redirect.
- Verify scope server-side on every request — the token tells you what it's allowed to do.
Implementation tips
- Use a library (`@auth/core`, Hydra, Auth0, Clerk, Keycloak). Don't implement the redirect-validate-exchange dance yourself.
- State parameter prevents CSRF on the callback — always set and verify.
- Redirect URIs must exactly match what's registered (incl. trailing slash). Most "mismatch" errors are this.
- HTTPS in production. `localhost` is the only non-HTTPS exemption most providers allow.