OAuth Authorization Code Flow with PKCE

The authorization code flow with PKCE is the recommended way for applications to obtain tokens on behalf of a user in OAuth 2.0, for web apps, single-page apps, mobile apps, and desktop apps alike. The user signs in at the authorization server (Google, Entra ID, Okta, Auth0, Keycloak, or your own), which redirects back to your app with a short-lived authorization code. Your app then exchanges that code, over a direct back-channel request, for access tokens (and optionally refresh and ID tokens).

PKCE (Proof Key for Code Exchange, pronounced "pixie") binds the code to the client that requested it, so an intercepted code is useless to an attacker. Once meant for mobile apps, PKCE is now recommended for all clients by the OAuth 2.1 draft and current security best practices. The older implicit flow, which returned tokens directly in the URL, is deprecated.

TL;DR

Quick Example

The flow, step by step:

Generating PKCE values (TypeScript):

In practice, use a certified library (for example openid-client, oauth4webapi, MSAL, AppAuth, or Spring Security) rather than hand-rolling the flow.

Core Concepts

Roles

Why a Code Instead of Tokens?

The front channel (browser redirects) is exposed: URLs leak through browser history, logs, referrer headers, and malicious extensions. The authorization code is short-lived (seconds to minutes), single-use, and useless without the back-channel exchange, which requires PKCE proof, and client authentication for confidential clients. Tokens therefore travel only over the direct TLS connection between the client and the token endpoint.

How PKCE Works

  1. The client creates a secret code verifier and sends only its SHA-256 hash (the code challenge) in the authorization request.
  2. The authorization server stores the challenge alongside the issued code.
  3. At token exchange, the client sends the verifier, the server hashes it, and it must match.

An attacker who steals the code (through a malicious app registered on the same custom URI scheme, a leaked log, or an open redirect) can't redeem it without the verifier. Always use S256, never plain.

state, nonce, and Redirect URIs

Client Types

Mobile and desktop apps should use the system browser (ASWebAuthenticationSession, Custom Tabs), not embedded webviews, following RFC 8252. Webviews let the app observe credentials, and they break SSO.

SPAs and the Backend-for-Frontend Pattern

A pure SPA can run the flow in the browser with PKCE, but then access and refresh tokens live in JavaScript, where any XSS can steal them. The BFF pattern is recommended for sensitive apps:

This removes tokens from the browser entirely. See XSS and CSRF for the related protections.

Best Practices

Use PKCE Everywhere

Even confidential server apps should use PKCE. It defends against code injection, and it's mandated by OAuth 2.1 and the OAuth Security Best Current Practice (RFC 9700).

Use Certified Libraries

OAuth has many subtle validation steps (state, PKCE, issuer, audience, nonce, token signatures). Use well-maintained, OpenID-certified libraries for your platform.

Request Minimal Scopes

Ask for only the scopes the app needs, when it needs them (incremental consent). Broad scopes increase damage if tokens leak, and they reduce user trust.

Consider PAR for Higher Security

Pushed Authorization Requests (RFC 9126) send authorization parameters via a back-channel POST and pass only a reference in the browser redirect, which prevents parameter tampering and leakage. They're required in high-security profiles like FAPI 2.0.

Common Mistakes

Using the Implicit Flow

response_type=token returns access tokens in the URL fragment, where they're exposed to history, referrers, and scripts. It's deprecated. Use authorization code plus PKCE instead.

Loose Redirect URI Matching

Registering https://app.example.com/*, or allowing arbitrary subdomains, lets attackers redirect codes to pages they control. Register exact URIs per environment.

Skipping state Validation

Without verifying state, attackers can trick users into completing a login bound to the attacker's account (login CSRF), or inject stolen codes.

FAQ

What is PKCE and why is it needed?

PKCE is an extension where the client proves, at token exchange, that it's the same client that started the authorization request, using a secret verifier and its hashed challenge. It prevents stolen authorization codes from being redeemed, and it's now recommended for all OAuth clients, not just mobile apps.

Should single-page apps use the authorization code flow?

Yes, with PKCE. The implicit flow is deprecated. For sensitive applications, go further with a backend-for-frontend, so tokens never reach browser JavaScript.

What's the difference between the authorization code and the access token?

The authorization code is a short-lived, one-time credential delivered via the browser that proves the user authorized the client. The access token, obtained by exchanging the code over a secure back channel, is what the client presents to APIs.

Does PKCE replace the client secret?

For public clients that can't keep secrets, PKCE is the protection. Confidential clients should use both: client authentication proves the client's identity, and PKCE binds the code to the specific authorization request.

Related Topics

References