OAuth 2.0 PKCE (Proof Key for Code Exchange) is an extension to the OAuth 2.0 authorization framework designed to enhance security for public clients such as single-page applications (SPAs), mobile apps, and desktop applications. PKCE mitigates authorization code interception attacks by adding a dynamic secret generated at runtime, ensuring that only the original client that initiated the authorization request can exchange the authorization code for an access token.
PKCE is now considered a best practice for modern OAuth 2.0 implementations and is widely recommended for both public and confidential clients.
Why Standard Authorization Code Flow Is Not Enough
In the traditional OAuth 2.0 Authorization Code flow:
The client redirects the user to the authorization server.
The user authenticates and grants consent.
The authorization server redirects back with an authorization code.
The client exchanges the code for an access token.
If an attacker intercepts the authorization code during redirection, they may exchange it for a valid access token. Public clients cannot securely store client secrets, making them particularly vulnerable.
PKCE adds a cryptographic verification step to prevent this attack.
How OAuth 2.0 PKCE Flow Works
The PKCE flow introduces two additional parameters:
Code Verifier
Code Challenge
High-level flow:
Client generates a random code_verifier.
Client creates a code_challenge by hashing the verifier.
Client sends the code_challenge to the authorization server.
Authorization server issues an authorization code.
Client sends the authorization code along with the original code_verifier.
Server validates that the hashed verifier matches the original challenge.
Access token is issued.
This ensures that even if the authorization code is intercepted, it cannot be exchanged without the original code_verifier.
Step-by-Step PKCE Implementation
Step 1: Generate Code Verifier
The code_verifier is a high-entropy cryptographically random string.
Example (JavaScript):
function generateCodeVerifier() {
const array = new Uint8Array(32);
window.crypto.getRandomValues(array);
return btoa(String.fromCharCode(...array))
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
}
Step 2: Create Code Challenge
Use SHA-256 to hash the verifier.
async function generateCodeChallenge(verifier) {
const encoder = new TextEncoder();
const data = encoder.encode(verifier);
const digest = await crypto.subtle.digest('SHA-256', data);
return btoa(String.fromCharCode(...new Uint8Array(digest)))
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
}
Step 3: Redirect to Authorization Server
Include parameters:
Step 4: Exchange Authorization Code for Token
Send POST request:
POST /token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=AUTH_CODE
&redirect_uri=REDIRECT_URI
&client_id=CLIENT_ID
&code_verifier=ORIGINAL_VERIFIER
The server validates that:
SHA256(code_verifier) == code_challenge
If valid, the access token is issued.
How PKCE Improves Security
PKCE protects against:
Authorization code interception
Man-in-the-middle attacks
Malicious app impersonation
Public client secret exposure risks
Because the code_verifier is never transmitted in the initial authorization request, attackers cannot reconstruct it.
Even if an attacker steals the authorization code, it is useless without the verifier.
PKCE vs Traditional Authorization Code Flow
| Feature | Authorization Code Flow | Authorization Code + PKCE |
|---|
| Client Secret Required | Yes (confidential clients) | Not required for public clients |
| Protection Against Code Interception | Limited | Strong |
| Mobile App Security | Weak | Strong |
| SPA Security | Weak | Strong |
| Recommended for Modern Apps | No | Yes |
PKCE strengthens the authorization process for modern distributed applications.
When Should You Use PKCE?
PKCE should be used for:
It is also increasingly recommended for confidential server-side applications as an additional security layer.
Common Implementation Mistakes
Using weak random strings for code_verifier
Not using S256 hashing method
Storing verifier insecurely
Allowing multiple token exchanges with same authorization code
Failing to validate redirect URIs
Strict adherence to the PKCE specification ensures maximum security benefit.
Summary
OAuth 2.0 PKCE (Proof Key for Code Exchange) is a security extension that enhances the Authorization Code flow by introducing a dynamically generated code verifier and corresponding hashed code challenge, preventing authorization code interception attacks. By ensuring that only the original client can exchange the authorization code for an access token, PKCE significantly improves security for public clients such as SPAs and mobile applications. As modern application architectures increasingly rely on distributed frontends and APIs, implementing PKCE has become a critical best practice for secure OAuth 2.0 authentication and authorization flows.