Skip to content

Checkpoint challenge

Original, hands-on practice. Never modeled on actual exam questions. This closes the chapter Acting as Someone: The Impersonation Flows, chapter 11 of Securing Salesforce: Identity and Access.

The Burger House delivers Priya’s standing order every Friday night, and it has to update her record in Salesforce as Priya, not as an integration user. She won’t be there.

Your terminal plays the Burger House. You’ll make its key and certificate, put the certificate on an app in Salesforce, allow exactly one person, build the golden pass yourself, and use it. The Salesforce CLI’s org login jwt does all of this for you (S2). Here you do it by hand, so you can see every piece.

  • A key pair: a private key that stays in your folder, and its certificate.
  • An external client app with the JWT bearer flow on, the certificate uploaded, and one pre-authorized permission set.
  • A JWT naming Priya, built and signed in the terminal.
  • An access token for Priya, by curl, and a change made with it.
  • A Developer Edition org, with a test user, Priya, who isn’t an admin and can edit an Account she owns.
  • A permission set, not a profile, on the app’s policy, holding only Priya.
  • The private key never leaves your folder. Don’t commit it, don’t email it, don’t paste it anywhere.
  • macOS or Linux, or Windows with Git Bash: the commands use bash, openssl and curl.

1. Have Priya ready. In your org, check Priya’s username, and that she owns an Account she can edit. Note the Account’s ID.

2. Make the Burger House’s key and certificate. In a new, empty folder:

Terminal window
openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout server.key -out server.crt -subj "/CN=Burger House lab"

server.key is the private key: the seal. server.crt is the certificate: what Salesforce will check the seal against. It’s well under the 4 KB limit (S1).

3. Make the permission set. In Setup, create a permission set called “Burger House: act as” and assign it to Priya, and nobody else.

4. Make the app (S2). From Setup, App Manager, click New External Client App. Give it a name, “Burger House”, and your email.

  • Under API (Enable OAuth Settings), click Enable OAuth. For the callback URL, enter http://localhost:1717/OauthRedirect: this flow never uses it, but the app needs one.
  • Choose the scope Manage user data via APIs (api).
  • In Flow Enablement, select Enable JWT Bearer Flow, and upload server.crt.
  • Click Create. Then Edit, the Policies tab, OAuth Policies: set Permitted Users to Admin approved users are pre-authorized, and under App Policies, select the permission set “Burger House: act as”. Save.
  • On the Settings tab, open OAuth Settings, click Consumer Key and Secret, verify your identity, and copy the consumer key. You won’t need the secret.

5. Build and sign the golden pass. In the same folder, put in your consumer key and Priya’s username, and run:

Terminal window
b64url() { openssl base64 -A | tr '+/' '-_' | tr -d '='; }
CLIENT_ID="YOUR_CONSUMER_KEY"
USERNAME="PRIYAS_USERNAME"
HEADER=$(printf '{"alg":"RS256"}' | b64url)
EXP=$(( $(date +%s) + 180 ))
CLAIMS=$(printf '{"iss":"%s","sub":"%s","aud":"https://login.salesforce.com","exp":%s}' "$CLIENT_ID" "$USERNAME" "$EXP" | b64url)
SIG=$(printf '%s.%s' "$HEADER" "$CLAIMS" | openssl dgst -sha256 -sign server.key | b64url)
JWT="$HEADER.$CLAIMS.$SIG"
echo "$JWT"

Read what you built. The first part, eyJhbGciOiJSUzI1NiJ9, is the header, exactly as in Salesforce’s example (S1). The second part is the claims: who it’s from (iss), who it’s for (sub), where it’s going (aud) and when it expires (exp, three minutes from now). The third part is the seal: the first two signed with server.key.

6. Present it. Within three minutes:

Terminal window
curl -s https://YOUR-DOMAIN.my.salesforce.com/services/oauth2/token -d grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer -d assertion="$JWT"

You should get back an access_token, an instance_url, and an id that ends in Priya’s user ID. Look for a refresh token. There isn’t one: “This flow never issues a refresh token” (S1). If Salesforce refuses the audience, check aud against the values Salesforce’s page for this flow lists (S1).

7. Do the job, as Priya. Update her Account with the token (the API version from https://YOUR-DOMAIN.my.salesforce.com/services/data):

Terminal window
curl -s -X PATCH "https://YOUR-DOMAIN.my.salesforce.com/services/data/v66.0/sobjects/Account/ACCOUNT_ID" -H "Authorization: Bearer YOUR_ACCESS_TOKEN" -H "Content-Type: application/json" -d '{"Description":"Friday delivery done"}'

Open the Account in Salesforce. Who’s in Last Modified By? Priya. Not an integration user, and not you.

8. Try to break it. Build two more JWTs and present each one. Each time, Salesforce answers with an error; its words can change from release to release, but it should point at what you broke.

  • Someone not on the list. Set USERNAME to your admin’s username and run steps 5 and 6 again. Salesforce refuses: the admin isn’t pre-authorized.
  • A tampered pass. Build a good JWT, then change one letter in the middle part (the claims) before you present it. Salesforce refuses: the seal no longer matches.

9. Put it away. On the app, turn off the JWT bearer flow or remove Priya from the permission set. Delete server.key when you’re done with the lab.

Why was there no refresh token? Because nobody’s there to have approved it, and the Burger House can always sign a new JWT. The access token runs out; the key doesn’t.

Why a permission set and not Priya’s profile? Because the app can act as anyone on its list. Put a profile there, and the Burger House can become everyone in that profile. A permission set with one person keeps the golden pass to one person.

What happens if someone copies server.key? They can sign a JWT naming anyone on the app’s list, from anywhere, with no password and no person. That’s why the key is the most important secret in this lab, and why in production it lives in a vault.

How is this different from chapter 8’s client credentials? There, the token belonged to the integration user, and every change showed the integration user. Here, the token belongs to Priya, and the change shows Priya.

Where else have you seen this flow? The Salesforce CLI’s org login jwt (S2), in every build pipeline that logs in to an org with nobody at the keyboard.

  • The app: JWT bearer on, the certificate uploaded, Admin approved users are pre-authorized, one permission set with only Priya.
  • The JWT: RS256, with iss, sub, aud and exp, signed with your private key.
  • The token: for Priya, with no refresh token.
  • The change: Last Modified By is Priya.
  • The refusals: the admin, and the tampered pass.
  • IDP-1 (choosing the OAuth flow): JWT bearer for a trusted server acting as a pre-authorized user.