Fieldnotes / engineering

OAuth PKCE

Explanation

Delegate access

OAuth lets a client obtain delegated access. The authorization server issues tokens that a resource API can validate.

OAuth is an access-delegation protocol. PKCE protects one part of an authorization-code flow; it is not a universal permission check.

  1. 0s A client needs access to an API. An authorization server handles the grant.
  2. 7s The user authorizes access in a browser. The client receives an authorization code.
  3. 14s The client exchanges the code for tokens. The server validates the exchange.
  4. 21s A token accompanies an API request. The API still checks access.

Create a verifier

For each authorization attempt, the client creates a fresh unpredictable verifier and retains it for that attempt. S256 derives a challenge from it.

The verifier is a secret for this transaction. A later login needs a different verifier and separate storage.

  1. 0s Start a new authorization attempt. Create a fresh unpredictable verifier.
  2. 7s Keep that verifier with attempt A. It will be needed at token exchange.
  3. 14s Derive its S256 challenge. The challenge is a transformed value.
  4. 21s Send the challenge first. Keep the verifier for the later exchange.

Bind code and challenge

The client sends its challenge with the authorization request. After authorization, the server issues a code bound to that challenge.

The first leg sends the challenge, not the verifier. Normal OAuth request validation and user authorization still apply.

  1. 0s The client has challenge A. Its verifier remains with the client.
  2. 7s Send challenge A for authorization. The server records it for this attempt.
  3. 14s After authorization, issue code A. The server binds it to challenge A.
  4. 21s A code alone is not the full proof. The exchange must match the challenge.

Prove the verifier matches

The client submits the authorization code and the original verifier. The authorization server derives a challenge from the verifier and compares it with the saved value.

A matching PKCE check allows the rest of token validation to proceed. A mismatch must fail; it should not disable the protection.

  1. 0s Submit code A with verifier A. The server has its saved challenge.
  2. 7s Derive S256 from the submitted verifier. Compare it with saved challenge A.
  3. 14s A matching proof passes this check. Other OAuth checks must also pass.
  4. 21s Then tokens may be issued. The code and proof belong together.

Separate login attempts

Two logins overlap. If B overwrites a global verifier, exchanging code A with verifier B fails. Store each verifier with its own transaction.

Repair transaction-specific storage and start a fresh login. Do not disable PKCE or repeatedly send mismatched credentials.

  1. 0s Attempt A owns code A and verifier A. Attempt B needs its own stored verifier.
  2. 7s A shared variable is overwritten by B. Code A is now paired with verifier B.
  3. 14s The server checks the mismatched proof. It must reject this token exchange.
  4. 21s Keep verifier storage per transaction. Repair the client; start a fresh login.
Read the guide

Helpful first: Guardrails and permissions, Tool design.

The idea

OAuth delegates access; an authorization code is exchanged for tokens. Proof Key for Code Exchange (PKCE) adds a fresh secret, the verifier, for each authorization attempt. The client sends its S256 challenge first and the verifier only during token exchange. Matching the challenge lets normal OAuth validation continue; it does not itself authorize every operation.

Where it helps

An intercepted code should be unusable without its verifier. This matters when a desktop agent host connects to a protected service through browser authorization. PKCE belongs in the host’s OAuth implementation, outside model-generated prompts. It also exposes ordinary engineering mistakes: concurrent logins can fail when a shared variable overwrites the verifier.

Read this when the team owns an agent connector’s login flow or reviews an OAuth integration. It supports safe delegation to protected services, but engineers using an already configured coding assistant do not need to implement PKCE first.

Illustrative scenario: a team connects its incident-summary assistant to an HTTP tool service. Two browser login attempts overlap. Callback A arrives after attempt B has overwritten a global verifier. Sending code A with verifier B fails the PKCE check. The repair is transaction-specific storage and a fresh login, not disabling the check or asking the model to retry credentials.

What you will be able to check

  • Trace a challenge, code and verifier through one authorization attempt.
  • Identify shared verifier storage that breaks concurrent logins.
  • Demonstrate rejection of a mismatched verifier and separate that check from tool permissions.

What changes in the team

Today
An engineer manually signs into a dashboard and copies incident details into a summary.
With an agent
An agent calls a narrowly scoped incident-reading tool after the host completes the user’s OAuth flow.
Still your responsibility
Engineers choose trusted services, review granted scopes, test credential handling, and enforce write restrictions in the service.

Work through an example

  1. Locate the actual OAuth client: in this example it is the desktop host, not the language model. Native applications should use an external browser for authorization, following RFC 8252. The user approves access there; the model does not need the login secret.
  2. Let the library generate a cryptographically random verifier for each attempt. RFC 7636 specifies 43–128 permitted characters and recommends generating 32 random octets before base64url encoding. S256 means base64url(SHA256(ASCII(verifier))) without padding. Store the verifier under that transaction; send only its challenge in the authorization request.
  3. At callback, retrieve the matching transaction and send its verifier with the code to the token endpoint over TLS. The server compares the derived challenge with the one associated with that code. A mismatch returns invalid_grant. A match proceeds to the remaining OAuth checks. Bind that transaction to the initiating client and browser session; a lookup keyed by an unchecked callback identifier is not sufficient.
  4. For the versioned MCP HTTP authorization flow cited here, clients must verify PKCE support before proceeding; capable clients use S256. MCP servers must validate that tokens are intended for them. This does not describe every agent transport: the specification distinguishes HTTP authorization from credentials supplied to STDIO processes.

The engineering decision

Use a maintained OAuth library and inspect its integration. RFC 9700 requires PKCE for public clients and recommends it for confidential clients; the advice is not limited to mobile apps. Verify redirect handling and transaction binding. Scope, intended resource, token storage, and operation-level authorization remain separate responsibilities. A persistent prompt saying “read only” is not an enforcement mechanism.

  • One global verifier lets the last login overwrite an earlier attempt’s secret.
  • Logging callback payloads or exposing verifiers and tokens in model context expands the credential exposure surface.
  • Treating PKCE success as permission for every tool action bypasses the separate authorization decision.

Try it with your team

Cross two login transactions in a test environment

  1. Use a test account, local callback and maintained OAuth library; start attempts A and B.
  2. Record only transaction identifiers and outcomes; confirm each attempt has distinct verifier storage.
  3. In the test harness, pair code A with verifier B and observe rejection without a token.
  4. Start fresh attempts and verify correct pairs succeed; review logs and confirm the read-only tool still rejects a write.

You have an answer when…

  • A crossed pair fails, while fresh correctly matched attempts succeed.
  • Logs contain no verifier or token, and server-side write denial remains independent of PKCE.

Read further

Can two overlapping logins complete with their own transaction records, while a deliberately crossed code/verifier pair fails without leaking secrets?

Original video comparisons