
RFC 7636: PKCE and the Authorization Code Interception Attack
RFC 7636 defines PKCE to prevent authorization code interception attacks in OAuth 2.0 flows for public clients.
OAuth 2.0 is the standard for authorization, but its original specification (RFC 6749) contained a critical flaw when applied to "public clients"—applications that cannot securely store a client secret. This includes Single Page Applications (SPAs) running in browsers and native mobile apps. The flaw allowed attackers to intercept the temporary authorization code and exchange it for a long-lived access token, effectively stealing the user's session. RFC 7636 introduced Proof Key for Code Exchange (PKCE) to close this gap.
Part 2 of the OAuth 2.0 RFCs Every Engineer Should Read series.
The Vulnerability: Interception of the Authorization Code
To understand PKCE, we must first understand the attack vector defined in RFC 6749 Section 4.1. In the standard OAuth 2.0 Authorization Code flow, the user authenticates with the provider (e.g., Google, GitHub) and is redirected back to the client app with a temporary authorization code. The client app then sends this code to the authorization server's token endpoint to receive an access token.
For "confidential clients" (server-side applications), this step is secured by requiring a client_secret. However, public clients cannot keep a secret. They expose their credentials in the client-side code or app bundle. Without a secret, the only thing protecting the authorization code is the secrecy of the code itself during the redirect.
If an attacker can intercept the authorization code—via a malicious app on the same device, a compromised network, or a referrer header leak—they can immediately call the token endpoint. Since the public client doesn't have a secret to prove its identity, the authorization server accepts the code exchange. The attacker now has a valid access token.
The Mechanism: Challenge and Verifier
PKCE solves this by introducing a cryptographic link between the initial authorization request and the final token request. It requires the client to generate two values: a code_verifier and a code_challenge.
- Code Verifier: As defined in RFC 7636 Section 4.1, the
code_verifieris a high-entropy cryptographic random string. RFC 7636 recommends using 43 to 128 characters from the unreserved URL characters:A-Z,a-z,0-9,-,.,_,~. - Code Challenge: As defined in RFC 7636 Section 4.2, the
code_challengeis derived from thecode_verifier. There are two methods:- plain: The
code_challengeis identical to thecode_verifier. This is simple but less secure if the channel is not encrypted (HTTPS). - S256: The
code_challengeis the base64url encoding of the SHA256 hash of thecode_verifier. This is the recommended method as it provides integrity even if the initial request is somehow observed.
- plain: The
The core mechanism is that the code_challenge is sent in the initial /authorize request. The authorization server stores this challenge associated with the pending authorization request. Later, when the client requests a token, it must provide the original code_verifier. The server then recomputes the challenge from the verifier and compares it to the stored value. If they match, the exchange is valid.
import hashlib
import base64
import secrets
def generate_pkce_params():
# Generate a random code_verifier
code_verifier = secrets.token_urlsafe(32) # 32 bytes = ~43 chars in base64url
# Compute the S256 code_challenge
sha256_digest = hashlib.sha256(code_verifier.encode('ascii')).digest()
code_challenge = base64.urlsafe_b64encode(sha256_digest).decode('ascii').rstrip('=')
return code_verifier, code_challengeThe Flow with PKCE
Let's trace the steps with named actors: Alice (user), MobileApp (public client), and AuthServer (OAuth provider).
- Preparation: MobileApp generates
code_verifierandcode_challenge(S256). It stores thecode_verifierlocally. - Authorization Request: MobileApp redirects Alice to AuthServer with
?code_challenge=...&code_challenge_method=S256. - User Consent: Alice authenticates and consents. AuthServer stores the
code_challengeand generates an authorization code. - Callback: AuthServer redirects Alice back to MobileApp with
?code=<authorization_code>. - Token Request: MobileApp sends a POST to the token endpoint with:
grant_type=authorization_codecode=<authorization_code>redirect_uri=...code_verifier=<code_verifier>
- Validation: AuthServer looks up the pending authorization request, retrieves the stored
code_challenge, and computes the SHA256 hash of the receivedcode_verifier. If the hashes match, it issues the access token.
If an attacker intercepted the authorization code in step 4, they would not have the code_verifier. When they try to exchange the code in step 5, the computed challenge will not match the stored one, and the server rejects the request.
Why PKCE is Mandatory for Public Clients
Before RFC 7636, many developers avoided the Authorization Code flow for public clients, opting for the Implicit Flow (which returns tokens directly in the URL fragment). However, the Implicit Flow is now deprecated in OAuth 2.1 (currently an IETF draft) because it exposes tokens in URLs and browser history, creating other security risks.
PKCE allows public clients to use the more secure Authorization Code flow without needing a backend secret. It shifts the security model from "proving identity via a secret" to "proving possession of a transient cryptographic key."
RFC 7636 is not optional for modern public clients. Major providers like Google, Facebook, and Microsoft require PKCE for OAuth 2.0 flows involving public clients. Failure to implement PKCE results in authentication failures or, worse, a false sense of security.
Conclusion
PKCE is a simple yet effective addition to OAuth 2.0. By binding the authorization request to the token request via a challenge/verifier pair, it prevents authorization code interception attacks on public clients. For backend and mobile developers, implementing PKCE is a straightforward change: generate a verifier, send the challenge, and submit the verifier at the token endpoint. It is a critical best practice for securing modern web and mobile applications.
Related posts
RFC 6749 Revisited: What Still Applies in 2026
An examination of RFC 6749 (OAuth 2.0) in 2026, analyzing which grant types remain relevant and why the implicit flow is deprecated.
Every Field in the RFC 8414 Metadata Document, Explained
A detailed examination of RFC 8414 metadata fields, including issuer, authorization_endpoint, and token_endpoint, for backend developers and identity engineers.
RFC 8414 Explained: OAuth 2.0 Authorization Server Metadata
RFC 8414 defines the Authorization Server Metadata protocol, enabling OAuth 2.0 clients to automatically discover endpoints and capabilities via a well-known URI.