Articles

The OAuth PKCE Flow Explained

PKCE hardens the OAuth authorization code flow against interception, and is now recommended for every client type, not just mobile and single-page apps.

Chisato Chisato · · 4 min read
Abstract illustration representing OAuth

PKCE (Proof Key for Code Exchange, pronounced “pixy”) is an extension to the OAuth 2.0 authorization code flow that prevents a stolen authorization code from being redeemed by anyone other than the client that requested it. It was originally designed for mobile and single-page apps that can’t safely hold a client secret, but it’s now recommended for essentially every client type, including traditional server-side web apps.

The problem PKCE fixes

The standard authorization code flow works like this: the client redirects the user to an authorization server, the user authenticates and approves access, and the authorization server redirects back to the client with a short-lived authorization code. The client then exchanges that code for an access token — usually by presenting a client_secret to prove it’s really the registered client.

That last step is where the trouble starts for public clients — mobile apps, single-page apps, and any client that can’t keep a secret genuinely secret. A client_secret embedded in a mobile app binary or shipped in JavaScript to a browser isn’t a secret at all; anyone can extract it. Worse, on mobile specifically, the redirect back to the app after authorization is handled via a custom URL scheme, which a malicious app on the same device can potentially register and intercept, stealing the authorization code before the legitimate app receives it.

Without a way to prove the code exchange request is coming from the same client that started the flow, an intercepted code plus a public client’s non-secret secret is enough to obtain an access token.

How PKCE closes the gap

PKCE has the client generate a per-flow secret at the start, and prove it holds that secret at the end — without ever exposing the secret itself over the network during the authorization step.

  1. Generate a code verifier. Before redirecting the user, the client generates a high-entropy random string, the code_verifier, and keeps it locally.
  2. Send a code challenge. The client hashes the verifier (typically with SHA-256) to produce a code_challenge, and includes that — not the verifier — in the initial authorization request.
  3. Authorization server stores the challenge. It’s bound to the authorization code it eventually issues.
  4. Redeem the code with the original verifier. When exchanging the authorization code for a token, the client sends the original code_verifier alongside it.
  5. Server verifies the hash matches. The authorization server hashes the verifier it just received and checks it matches the code_challenge from step 2. If they match, the token is issued; if not, the request is rejected.

An attacker who intercepts the authorization code from the redirect doesn’t have the code_verifier — it was never transmitted, only its hash — so they can’t complete the exchange even with a stolen code.

Why it’s the default now, not just for mobile

PKCE was written into the spec with public clients in mind, but the security community’s guidance has since shifted to recommending it for confidential clients too — traditional server-side apps that do hold a real client secret. The reasoning is defense in depth: PKCE protects specifically against authorization code interception, which is a distinct threat from client impersonation that client_secret protects against. The two mechanisms cover different attack surfaces, and using both costs almost nothing — most modern OAuth libraries and identity providers implement PKCE by default for every flow now, regardless of client type.

PKCE vs implicit flow

Older OAuth guidance for browser-based apps pointed toward the implicit flow, which skipped the authorization code exchange entirely and returned an access token directly in the redirect URL. That approach is now considered obsolete precisely because of the risks PKCE addresses more safely:

Implicit flow (legacy)Authorization code + PKCE
Token exposureAccess token in the URL fragmentToken never appears in a redirect URL
Refresh tokensNot supportedSupported
Interception riskToken itself can leak via browser history, referrersOnly a single-use code can leak; useless without the verifier
Current recommendationDeprecatedStandard for all client types

PKCE is a hardening of one specific OAuth grant type — it doesn’t replace OAuth itself, and it isn’t an authentication protocol on its own. It’s commonly deployed as part of an OpenID Connect flow, since OIDC is layered on top of OAuth’s authorization code grant; see our comparison of OAuth, OIDC, and SAML for how those pieces relate. The token issued at the end of a PKCE-protected flow is very often a JWT, and once a user has a valid session, longer-lived authentication increasingly relies on passkeys rather than passwords for the initial login step that kicks the flow off.

The takeaway

PKCE adds a per-flow, locally generated proof — a verifier and its hash — that ties an authorization code’s redemption to the exact client that requested it, closing the interception gap that made public clients unsafe under the plain authorization code flow. It costs one extra hash computation and one extra parameter on two requests, and current guidance is to use it everywhere, not just where a client secret is impossible to protect. If you’re implementing OAuth today and your library or identity provider supports PKCE by default, there’s no good reason to turn it off.

Chisato Chisato · · 5 min read

What Is Session Fixation?

Session fixation tricks a victim into using an attacker-known session ID, so logging in hands the attacker an authenticated session too.

#Security #Authentication #Web Development
Chisato Chisato · · 4 min read

Cookie Attributes Explained: HttpOnly, Secure, SameSite

HttpOnly, Secure, and SameSite are cookie attributes that block script access, force HTTPS, and limit cross-site sending. Here's what each one actually stops.

#Security #Web Development #Authentication