PKCE Implementation
Implementing PKCE in Real-World Scenarios¶
The Proof Key for Code Exchange (PKCE) flow is critical for securing public clients (e.g., mobile apps, SPAs) in OAuth 2.0. By mitigating the risk of authorization code interception, PKCE ensures that even clients without a client secret can securely exchange authorization codes for tokens. Below are practical examples and configuration guidelines for integrating PKCE in mobile and single-page applications (SPAs).
📱 Mobile App Implementation¶
1. Authorization Request¶
Mobile apps generate a code_verifier and derive a code_challenge using SHA256. The code_challenge is sent to the authorization endpoint.
// Swift (iOS) - Generate code_verifier and code_challenge
func generateCodeChallenge() -> (String, String) {
let codeVerifier = "random32CharacterString"
let data = codeVerifier.data(using: .utf8)!
let codeChallenge = data.sha256().base64EncodedString()
return (codeVerifier, codeChallenge)
}
Authorization Request Example:
GET https://auth.example.com/oauth/authorize
?response_type=code
&client_id=your_client_id
&redirect_uri=your_redirect_uri
&scope=openid%20email
&code_challenge=base64sha256
&code_challenge_method=SHA253
2. Code Exchange¶
After user authentication, the app receives an authorization code and exchanges it for tokens using the code_verifier.
// Swift - Exchange code for tokens
let tokenRequest = URLRequest(url: URL(string: "https://auth.example.com/oauth/token")!)
var request = tokenRequest
var body = [URLQueryItem(name: "grant_type", value: "authorization_code")]
body.append(URLQueryItem(name: "code", value: "authorization_code"))
body.append(URLQueryItem(name: "redirect_uri", value: "your_redirect_uri"))
body.append(URLQueryItem(name: "code_verifier", value: "code_verifier"))
request.httpMethod = "POST"
request.allHTTPHeaderFields = ["Content-Type": "application/x-www-form-urlencoded"]
🌐 Single-Page Application (SPA) Implementation¶
1. Authorization Request¶
SPAs use the code_challenge in the authorization request, ensuring the code is tied to the client.
// JavaScript (SPA) - Generate code_verifier and code_challenge
async function generateCodeChallenge() {
const codeVerifier = crypto.randomBytes(32).toString('base64');
const codeChallenge = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(codeVerifier));
return [codeVerifier, Buffer.from(codeChallenge).toString('base64')];
}
Authorization Request Example:
GET https://auth.example.com/oauth/authorize
?response_type=code
&client_id=your_client_id
&redirect_uri=your_redirect_uri
&scope=openid%20email
&code_challenge=base64sha256
&code_challenge_method=SHA256
2. Code Exchange¶
The SPA sends the code_verifier to the token endpoint to exchange the code for tokens.
// JavaScript - Exchange code for tokens
const code = "authorization_code";
const codeVerifier = "code_verifier";
const tokenResponse = await fetch("https://auth.example.com/oauth/token", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "authorization_code",
code: code,
redirect_uri: "your_redirect_uri",
code_verifier: codeVerifier
})
});
🔐 Authorization Server Configuration¶
Ensure your authorization server supports PKCE and enforces these parameters:
- response_type=code
- code_challenge (base64-encoded SHA256 hash)
- code_challenge_method=SHA256
Example Configuration (Keycloak):
auth-server-url: "https://auth.example.com/auth"
realm: "my-realm"
client-id: "spa-client"
public-client: true
redirect-uri: "https://spa.example.com/callback"
🛡️ Security Considerations¶
- Secure Storage: Store
code_verifierin HTTP-only cookies or secure storage (e.g., Android Keystore) to prevent XSS. - No Client Secret: Public clients do not use client secrets; rely on PKCE for security.
- HTTPS: Always use HTTPS to prevent interception of the authorization code and
code_verifier.
Key takeaways¶
- PKCE is essential for public clients to prevent authorization code interception.
- Mobile apps and SPAs generate and use
code_verifierandcode_challengein the authorization and token exchange flows. - Authorization servers must validate
code_challengeand support PKCE parameters. - Secure storage of
code_verifierand HTTPS are critical to maintaining PKCE security.