Identity for AI

Proof Key for Code Exchange (PKCE) in the AuthPlayground

Protecting the authorization code from interception attacks.

When Sarah asks her AI assistant to prepare a meeting briefing for Apex Logistics, the assistant needs data from Salesforce, ServiceNow, and other enterprise systems. Before that happens, the application must establish Sarah’s authenticated session through an OAuth Authorization Code flow.

What if someone intercepts the short-lived authorization code? Can they redeem it, instead of the client that started the flow? PKCE (RFC 7636) protects against this by requiring the client redeeming the code to prove knowledge of a secret created when the flow began.

The PKCE model

The client creates a high-entropy random value called the code_verifier. Using S256, it derives the code_challenge:

"code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))"

The client uses the two values at different points:

  • Authorization request: The client sends the code_challenge. The verifier itself isn’t sent in the authorization request.

  • Token request: The client sends the code_verifier.

When the client redeems the code, the authorization server derives the challenge from the submitted verifier and compares it with the challenge associated with the original request. An intercepted authorization code alone isn’t enough.

The AuthPlayground implementation

Entities involved: Client application, IdP (authorization server)

The AuthPlayground demonstrates the Proof Key for Code Exchange flow in three steps:

  1. Generate code_verifier and code_challenge.

  2. Send the authorization request with code_challenge.

  3. Redeem the code with the code_verifier.

Step 1: Generate the verifier and challenge

The first step happens locally without a network request. The playground generates a code_verifier and derives the code_challenge using SHA-256 and Base64url encoding:

{
  "code_verifier": "zMmfa-o_LRRLZJRDwXfvZ5oyrF4K__IJnwoD9SUaZtQ",
  "code_challenge": "FSm-7wdqeus-41oeOeSjglqXdnSt-LhmvRwJgzR59DY",
  "code_challenge_method": "S256"
}

The client keeps the code_verifier and sends only the derived challenge during authorization.

Step 2: Send the code_challenge

The authorization request includes the following information in the query string:

GET /idp/authorize
    response_type=code
    client_id=playground-demo-client
    redirect_uri=https://.../callback
    scope=openid profile email
    code_challenge=FSm-7wdqeus-41oeOeSjglqXdnSt-LhmvRwJgzR59DY
    code_challenge_method=S256

The authorization server returns an authorization code through the browser redirect:

302
location: https://.../callback

{
  "code": "<authorization_code>",
  "state": "<state>",
  "note": "Authorization Response: code returned via redirect_uri (RFC 6749 §4.1.2)"
}

At this point, the authorization code has traveled through the redirect. The original code_verifier hasn’t. That separation is the core of PKCE.

Step 3: Redeem the code with the code_verifier

The legitimate client sends both the authorization code and its original verifier:

POST /idp/token
Content-Type: application/x-www-form-urlencoded

{
  "grant_type": "authorization_code",
  "code": "<authorization_code>",
  "redirect_uri": "https://.../callback",
  "client_id": "playground-demo-client",
  "code_verifier": "RvtACdjYxEsvE_kEJ6W6w3WsrG2nKZUtfepOY5EosFQ"
}

The authorization server derives the challenge from the submitted verifier and compares it with the challenge associated with the authorization request. The authorization server:

  1. Receives the code_verifier.

  2. Calculates the challenge using SHA-256 and Base64url encoding.

  3. Compares the calculated challenge with the original code_challenge.

  4. Returns tokens if they match, or rejects the request if they don’t.

In the playground flow, the values match and the authorization server returns an access token and ID token for Sarah (demo-user@example.com).

How PKCE compares to other concepts

Bringing it back to enterprise AI

Sarah’s request might eventually cause the AI assistant to reach Salesforce opportunity data or ServiceNow support history. PKCE operates much earlier in the flow:

  • Sarah initiates the request to the AI assistant application.

  • The application performs the authorization code flow with PKCE against the authorization server.

  • The authorization server issues the tokens.

  • The AI workflow uses the tokens for downstream integrations such as Salesforce or ServiceNow.

PKCE doesn’t decide what Salesforce or ServiceNow will allow. It doesn’t establish workload identity or carry delegated identity across authorization domains. Its job is narrower. PKCE protects authorization-code redemption to ensure that possession of an intercepted code alone isn’t enough to obtain the resulting tokens.

PKCE and PAR

PKCE and Pushed Authorization Requests (PAR) are often used together, but they protect different parts of the flow:

  • PAR protects how the authorization request reaches the server.

  • PKCE protects authorization-code redemption.

A deployment might use one or both depending on the requirements.

Key takeaways

The complete PKCE flow looks like this:

  1. Generate code_verifier.

  2. Derive code_challenge.

  3. Authorize with code_challenge.

  4. Receive authorization_code.

  5. Redeem with code_verifier.

  6. Receive tokens.

The important security property of PKCE is that obtaining the authorization code by itself isn’t enough to redeem it. For an AI workflow that might reach many enterprise systems, PKCE gives the rest of the authorization chain a stronger starting point.