Skip to main content
Security

OAuth 2.0 flows: authorization code with PKCE, and client credentials

In 2026 there are two flows you should reach for and several you shouldn't. A worked PKCE exchange with real values, plus the questions that trip teams up.

Thien Nguyen
By Thien Nguyen
Updated July 21, 2026 · 3 min read

There are two OAuth flows you should be using, and the choice between them comes down to one fact: whether a human is delegating access or a machine is acting as itself. Authorization code with PKCE covers everything a person logs into — SPAs, mobile, desktop, and confidential server-side web apps. Client credentials covers service-to-service calls where no user is in the loop. The implicit flow and the resource-owner password grant are removed in OAuth 2.1; if a tutorial hands you either, it's teaching you something the spec is actively deleting.

SituationFlowWho the token represents
User signs into a SPAAuthorization code + PKCEThe user
Mobile app opens the provider loginAuthorization code + PKCEThe user
Server-side web app, has a client secretAuthorization code + PKCEThe user
Nightly service-to-service syncClient credentialsThe service itself

PKCE, with real values

PKCE ("pixie") stops an intercepted authorization code from being redeemed by an attacker. The client invents a secret, sends only a hash of it up front, then reveals the secret at redemption — so a stolen code is worthless without it.

StepValue
1. Generate code_verifierdBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk (43–128 chars)
2. Derive code_challengeE9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
3. MethodS256BASE64URL(SHA256(verifier))
4. Authorize request.../authorize?response_type=code&code_challenge=E9Mel…&code_challenge_method=S256
5. Provider redirects back?code=SplxlOBeZQQ…
6. Token requestPOST /token with code=SplxlOBeZQQ…&code_verifier=dBjftJeZ4CVP…

Takeaway: the challenge on the wire in step 4 is a one-way hash. Even if an attacker captures both the challenge and the code, they can't reverse the hash to produce the code_verifier step 6 requires. That's the entire security property, and it's why PKCE is now recommended for every client, secret or not.

FAQ

Do I still need PKCE if my app has a client secret?

Yes. The OAuth 2.0 Security Best Current Practice recommends PKCE for all authorization-code clients, confidential ones included. It defends against authorization-code injection, which a client secret alone doesn't stop.

Is the implicit flow really dead?

For practical purposes, yes. It returned tokens directly in the URL fragment, where they leaked through history and referrers. OAuth 2.1 removes it; use authorization code with PKCE, which now works cleanly in browsers thanks to CORS.

Can a SPA hold a refresh token?

Cautiously. Use short-lived access tokens plus refresh-token rotation, so a leaked refresh token is single-use and detectable. Many teams still prefer a lightweight backend-for-frontend that keeps tokens off the browser entirely.

What about the password grant for our own first-party app?

Skip it. Resource-owner password credentials is also gone in OAuth 2.1 — it trains users to type their password into non-provider UIs and can't support MFA or federated login. Route even first-party apps through the authorization endpoint.

For client credentials, request only the machine scopes you need and rotate the client authentication material on a schedule. A service token standing in for a human wrecks your audit trail in exactly the incidents where you'll wish you had one.

Building the authorize request by hand is where subtle bugs hide — a missing code_challenge_method, a redirect URI that doesn't match exactly. The OAuth authorization URL builder assembles a correct one so you can confirm the shape before wiring it into code.

Cover photo by cottonbro studio on Pexels.

References

Primary documentation and specifications checked when this article was last updated.

SecurityOAuthAPIs

Related articles

All articles