OAuth is the protocol that lets you click "Sign in with Google" without giving a third-party app your Google password. Understanding it properly matters because OAuth is also how your own API grants delegated access to clients — and the subtle mistakes in OAuth implementations are responsible for a disproportionate share of security incidents.
What changed in 2026
- OAuth 2.1 is the working standard. It consolidates 2.0 and removes the dangerous implicit and password grant flows. If you're starting a new implementation, target 2.1.
- PKCE is mandatory for all public clients. The Proof Key for Code Exchange extension (originally for mobile) is now required for all Authorization Code flows, including server-side web apps.
- DPoP (Demonstrating Proof of Possession) became mainstream. DPoP binds tokens to a specific key, making stolen bearer tokens useless — major identity providers now support it.
- The BFF pattern standardized. Browser applications that need OAuth tokens now commonly use a Backend-for-Frontend server to hold tokens, eliminating token exposure in JavaScript.
The flows that matter in 2026
| Flow |
Use case |
Status |
| Authorization Code + PKCE |
Browser apps, mobile, server apps |
Use this for everything |
| Client Credentials |
Machine-to-machine (no user) |
The right choice for service APIs |
| Device Authorization |
TV, CLI, IoT (no browser) |
Use for input-constrained devices |
| Implicit |
Browser apps |
Removed in OAuth 2.1 — do not use |
| Resource Owner Password |
Legacy integrations |
Removed in OAuth 2.1 — do not use |
Authorization Code + PKCE step by step
1. App generates a random code_verifier (64 bytes, base64url-encoded)
2. App computes code_challenge = BASE64URL(SHA256(code_verifier))
3. App redirects browser to authorization server:
GET /authorize
?response_type=code
&client_id=my-app
&redirect_uri=https://myapp.com/callback
&scope=openid profile email
&state=<random_csrf_token>
&code_challenge=<challenge>
&code_challenge_method=S256
4. User authenticates and consents
5. Auth server redirects back: /callback?code=<auth_code>&state=<same_state>
6. App exchanges code for tokens (server-side or native, never in-browser JS):
POST /token
grant_type=authorization_code
&code=<auth_code>
&redirect_uri=https://myapp.com/callback
&client_id=my-app
&code_verifier=<original_verifier> ← proves possession
7. Auth server returns: access_token, refresh_token, id_token (if OIDC)
The state parameter prevents CSRF. The code_verifier / code_challenge pair prevents authorization code interception.
OAuth vs OpenID Connect
OAuth 2.0 answers "can this app access this resource on behalf of the user?" It says nothing about who the user is.
OpenID Connect (OIDC) is an identity layer on top of OAuth. It adds an id_token (a signed JWT with user claims) and a /userinfo endpoint. If you need "who is this user" — for login — use OIDC. If you need "can this app do X" — for API authorization — use OAuth.
How to pick
- Single-page app or mobile app accessing your API? Authorization Code + PKCE. The access token lives in memory (not storage) or in HTTP-only cookies via a BFF.
- Server-to-server API access (no user)? Client Credentials flow. Use short-lived tokens and rotate client secrets regularly.
- Need login (authentication)? Add OIDC to your Authorization Code flow. Parse the
id_token; don't use the access token as proof of identity.
- Building an identity provider? Use Keycloak, Auth0, or Ory Hydra. Don't build your own unless you are a dedicated identity team.
- CLI tool? Device Authorization flow. The user authenticates in a browser; the CLI polls for the token.
Common mistakes
Using the access token as an identity token. The access token tells you what the bearer can do. The id_token (OIDC) tells you who they are. Conflating them is a security error.
Storing tokens in localStorage. XSS in any script on your page can read localStorage. Use HTTP-only cookies (for server-rendered or BFF apps) or in-memory storage with short expiry for SPAs.
No token expiry validation. Always check exp (expiry), iss (issuer), and aud (audience) when validating tokens. Libraries do this; rolling your own JWT parsing usually doesn't.
Ignoring the state parameter. The state parameter must be validated on callback to prevent CSRF attacks. Generate a random value, store it in session, and reject callbacks where it doesn't match.
Long-lived access tokens. A 24-hour access token gives an attacker a 24-hour window after theft. Use 15-minute access tokens with refresh tokens. Revoke refresh tokens on logout.
What to skip
- Rolling your own authorization server. Auth0, Okta, Keycloak, and AWS Cognito exist and are battle-tested. Custom auth servers are expensive to build, harder to audit, and error-prone.
- Implicit flow — removed in OAuth 2.1 for a reason. If your library still uses it, upgrade or switch libraries.
- The password grant for first-party apps — just use Authorization Code + PKCE with your own authorization server; it's equally seamless and much safer.
FAQ
What is the difference between OAuth and SAML?
Both handle federated identity. SAML is XML-based and dominant in enterprise SSO (ADFS, Okta SAML). OAuth/OIDC is JSON/JWT-based and dominant in consumer apps and APIs. Many platforms support both; prefer OIDC for new integrations.
Do I need a refresh token?
If your access tokens are short-lived (they should be), yes. Refresh tokens let the app obtain new access tokens without re-prompting the user. Store them securely, rotate them on each use, and revoke on logout.
Can I use OAuth for mobile apps?
Yes — Authorization Code + PKCE is specifically designed for mobile. Use a proper OAuth library (AppAuth for iOS/Android), and use a custom URI scheme or HTTPS redirect URI.
What is token introspection?
Token introspection (RFC 7662) lets a resource server validate an opaque access token with the authorization server. JWTs can be validated locally (faster, no network call); opaque tokens require introspection.
Where to go next
JWT best practices in 2026, Content security policy in 2026, and API rate limiting in 2026.