Proof Key for Code Exchange (PKCE)
Suppose someone obtains the authorization code while it is returning to the printer. Should possessing that code be enough to exchange it for an access token?
Proof Key for Code Exchange, usually called PKCE and pronounced "pixie," adds a proof tied to the individual authorization attempt. The client prepares that proof before sending the browser away.
Preparing a verifier and challenge
The client generates a fresh secret called the code_verifier. A real verifier uses cryptographically secure randomness and is between 43 and 128 characters long, using letters, digits, and the allowed characters -, ., _, and ~.
For teaching only, we will use this predictable value so the calculation can be repeated:
code_verifier:
btl_training_verifier_for_one_attempt_only_2026
code_challenge:
qd-75t-gnweZAhIl6REjxxgFnPXGwvqYcxq3vlsaJYQ
code_challenge_method:
S256
Do not use that fixed verifier in a real client. Matching the required length does not make a predictable string a secure secret.
With S256, the client hashes the verifier using SHA-256 and encodes the resulting bytes using Base64url without padding:
challenge = BASE64URL_WITHOUT_PADDING(SHA256(ASCII(verifier)))
This is a hash transformation, not encryption. The server does not decrypt the challenge to recover the verifier.
Proving possession later
The client sends the challenge and method in the authorization request. It keeps the verifier with the pending transaction.
When the authorization server issues a code, it associates that code with the challenge. Later, the client sends the code and verifier to the token endpoint. The server applies the same transformation to the received verifier and compares the result with the challenge associated with the code.
An incorrect verifier makes the exchange fail. Copying the visible challenge does not give an attacker the verifier needed to produce that challenge.
Understanding what the proof means
The verifier belongs to one attempt. It is not the printer's long-term client secret and does not establish the client's registered identity. Our confidential backend authenticates itself separately when making the token request.
Use PKCE with S256 in the flow taught here. Modern security guidance requires PKCE for public clients and recommends it for confidential clients as well. A backend holding a client secret still benefits from binding the exchange to the original attempt.
PKCE is not a complete defense against every form of credential theft. If an attacker compromises the client and obtains both the code and verifier, this particular check cannot distinguish their request from the client's. It also does not protect an access token after that token has been issued. Later security lessons will examine those different threats.