Skip to content

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_verifier in 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_verifier and code_challenge in the authorization and token exchange flows.
  • Authorization servers must validate code_challenge and support PKCE parameters.
  • Secure storage of code_verifier and HTTPS are critical to maintaining PKCE security.