Skip to content

The Pickup Word

Module 3 of 5 in the chapter The Burger House: Flows With a Person, chapter 9 of Securing Salesforce: Identity and Access. Each module stands on its own.

The question this module answers: What was the web server flow’s weakness, and how does PKCE close it?

Get the web server flow down first; then I’ll show you its weakness, and how it was fixed.

The weakness is this little code. In step 3, Salesforce hands the code to Priya’s browser, and her browser hands it to the Burger House. For a moment, milliseconds to a few seconds, the code is sitting in her browser before the Burger House presents it.

And what could a bad actor do with it, if they caught it in that moment? Present it themselves, and swap it for a token. That’s what people found a way to do. Picture it at the festival’s counter: you get your claim ticket for your drink, you turn toward the cash register, and somebody grabs the ticket out of your hand, runs to the register, and gets your drink first. They beat you there.

Module 1 said the swap also needs the Burger House’s secret. That protects a server-side app. But an app that runs on a phone or in a browser can’t keep a secret: Salesforce puts it plainly, “the client secret is accessible and exploitable because client executables reside on the user’s device” (S9). And even with a secret, the code was the one thing in the browser that could be stolen. We needed a way to lock the code to the Burger House, so nobody else could use it.

The fix is called PKCE, Proof Key for Code Exchange, pronounced “pixy”. Integration Architect gives it a picture, and this course uses the same one.

The pickup word: PKCE The same four lanes and five steps, with the pickup word. Step 1: the Burger House writes a pickup word and keeps it, and sends only its fingerprint. Step 2: the authorize request carries the fingerprint, the code_challenge; Salesforce keeps it with her request while Priya signs in, and redirects her back with a code. Step 3: the browser brings the code back. Step 4: the Burger House swaps the code with its secret and the pickup word, the code_verifier; the token endpoint checks the word matches the fingerprint, and the check passes. Step 5: the Burger House fetches her data. Footnote: a code copied on the way has no pickup word, so it gets nothing. {"author": "Steve Simpson", "blueprint": "1.1.0", "builder": "standard 1.58.0", "built": "2026-11-04", "id": "DGM-IAM-FWP-02", "rights": "\u00a9 2026 Twinshark LLC. All rights reserved.", "template": "cells 0.5.0", "theme": "stevetecharc 1.4.0"} PRIYA'S BROWSER THE BURGER HOUSE SALESFORCE: THE ENTRANCE SALESFORCE: TOKEN ENDPOINT AND API 1. Orders a burger and her drink her drink is at the food festival: Salesforce Needs her drink writes a pickup word and keeps it; sends only its fingerprint 2. Goes to Salesforce redirected, with the request The festival's entrance keeps the fingerprint with her request 3. Brings the code back to the Burger House's callback address Has the code a short-lived code, not a token 4. Swaps the code server to server: the back channel The token endpoint checks the word matches the fingerprint 5. Fetches her drink her data, acting as Priya, wearing her wristband The API what Priya may see a burger + her drink redirect to Salesforce + the fingerprint (code_challenge) redirect + the code the code burger + drink, after 5 code + secret + the pickup word (code_verifier) access + refresh token the wristband her data A code copied on the way has no pickup word, so it gets nothing. HOW TO READ IT Salesforce: the only place Priya gives her password the check passes © 2026 Twinshark LLC · stevetecharc.com
Click to enlarge

Before the Burger House sends Priya to Salesforce, it writes a pickup word on a slip of paper and keeps the slip. It sends Salesforce only the word’s fingerprint. Salesforce keeps the fingerprint with her request.

When the Burger House comes back to swap the code, it has to show the slip. Salesforce checks that the word matches the fingerprint. Only then does it hand over the tokens.

Now go back to the cash register. The thief grabbed the claim ticket, the code. But they don’t have the slip. They show up with the code and no pickup word, and they get nothing. The one that asked at the beginning has to be the same one that comes back at the end.

It’s just a couple of extra lines (S1):

In the requestWhat it is
The pickup wordcode_verifier, in the token request (step 4)“128 bytes of random data with high entropy”, made fresh each time
Its fingerprintcode_challenge, in the authorize request (step 2)“the SHA256 hash value of the code_verifier”, base64url-encoded

So the authorize request from module 2 gains one line:

code_challenge=*******************

And the token request gains another:

code_verifier=*******************

If they don’t match, Salesforce refuses: the login “fails with the invalid_grant error code” (S1).

Why is it safe to send the fingerprint through the browser, where anyone might see it? Because a fingerprint is a hash, from chapter 3. You can make the hash from the word in an instant, but you can’t run it backwards. Hashes are one-way. Seeing the fingerprint tells you nothing about the word, so the slip stays secret even though its fingerprint has travelled through the front channel.

For years PKCE was an option. Now:

  • New apps need it. From July 2026, Salesforce requires PKCE for the authorization code grant on new external client apps and connected apps, turned on for every new app from August 2026 (S3).
  • ISV apps need it. “The use of PKCE to protect OAuth Authorization Code login flows is now required for all ISV Salesforce client apps… it is now considered an essential industry security practice for OAuth 2.0” (S2).
  • An org can require it for every app. In Setup, OAuth and OpenID Connect Settings, turn on Require Proof Key for Code Exchange (PKCE) (S2).

For an existing app, there’s an order to it. Salesforce’s caution: “enabling PKCE requires a coordinated change on both the server and clients” (S2). Change the app’s code to send the challenge and verifier first, then require PKCE on the app in Salesforce, and only when every app is done, turn on the org-wide setting. Do it the other way round, and the apps stop working. Test it in a sandbox first.

Test knowledge. In my experience, know what PKCE protects against (a stolen authorization code), which value goes where (the challenge in the authorize request, the verifier in the token request), and that the challenge is a hash of the verifier.

Module 4: who is she? OpenID Connect.

  • PKCE on every app that uses the web server flow, new or old.
  • The order of the change for existing apps: the app’s code, then the app’s setting, then the org.
  • When to turn on Require PKCE for the whole org.