The Golden Pass
Module 2 of 5 in the chapter Acting as Someone: The Impersonation Flows, chapter 11 of Securing Salesforce: Identity and Access. Each module stands on its own.
The question this module answers: How does the JWT bearer flow let a trusted system act as someone, and what stops it acting as anyone?
An approved vendor
Section titled “An approved vendor”Back to the Burger House, with nobody home. For the festival to hand over Priya’s drink without Priya, the Burger House has to become something special: an approved vendor. The festival has to trust it completely.
How did we build that kind of trust in single sign-on? With a certificate. The identity provider kept its private key, Salesforce held its certificate, and a signed assertion was enough (chapters 3 and 6). The JWT bearer flow does exactly the same thing. The Burger House keeps a private key. Its certificate goes on the app in Salesforce. And from then on, a message signed with that key is believed.
The message is a JWT, a JSON Web Token, and people say “jot”. Think of it as a sealed envelope. Inside, it says: let me in as Priya. The seal is the Burger House’s signature. Salesforce opens it, checks the seal against the certificate, and lets them in as Priya. No person involved. It’s a golden pass.
The flow
Section titled “The flow”Same cells as module 1. Here’s what JWT bearer puts in them.
- Before anything: the Burger House makes a key pair and uploads the certificate to its external client app in Salesforce, with Enable JWT Bearer Flow turned on (S2). Salesforce wants the JWT “signed using RSA SHA256”, and the certificate can’t be bigger than 4 KB (S1).
- When the order is due, the Burger House builds a JWT naming Priya and signs it with its private key.
- It posts the JWT to the token endpoint. Salesforce checks the signature against the certificate, checks the claims, checks that Priya is allowed, and issues an access token (S1).
- It fetches her drink, as Priya.
Inside the envelope
Section titled “Inside the envelope”A JWT is three parts, each base64url-encoded, joined with dots: a header, the claims, and the signature. The header just says how it’s signed: {"alg":"RS256"}. The claims say who and for whom (S1):
| Claim | What it holds |
|---|---|
iss | The issuer: “the OAuth client_id of the external client app” with the certificate |
sub | The subject: the username of the person to act as, Priya |
aud | The audience: https://login.salesforce.com, or https://test.salesforce.com for a sandbox, or the site’s URL for an Experience Cloud site |
exp | When it expires, in seconds since 1970; Salesforce allows “a 3-minute buffer for clock skew” |
Then the signature: the header and claims, signed with the private key. Change one character inside, and the signature no longer matches.
One more claim is worth knowing. A JWT ID, jti, is optional, but “if you pass a JTI claim… Salesforce validates that the JTI claim hasn’t been sent before. This validation prevents JWT replay attacks” (S1). Send one, and a copied JWT can’t be used twice.
On the wire
Section titled “On the wire”The request is one POST to the token endpoint (S1):
POST /services/oauth2/tokengrant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOi...That’s chapter 8’s base pattern again: present something, get a wristband. Only now what’s presented is the signed JWT, not a client secret.
Notice two things that aren’t there. There’s no scope: “You can’t specify scopes in a JWT bearer token flow”; the scopes come from the app (S1). And the answer has an access token, the instance_url and Priya’s id, but “This flow never issues a refresh token” (S1). When the token runs out, sign a new JWT.
Who it may act as
Section titled “Who it may act as”Here’s what makes it powerful, and dangerous. Whoever holds the private key can sign a JWT naming anyone. So what stops the Burger House from becoming anybody at all?
Prior approval. Salesforce only issues the token for users the app is approved for (S1). On an external client app, set Permitted Users to Admin approved users are pre-authorized, and then choose the profiles and permission sets that are pre-authorized (S2).
Now think about what that means. Put a profile on that list, and the Burger House can impersonate anyone in that profile. Every one of them. Put System Administrator on it, and it can be any admin.
So keep it narrow:
- Use a permission set, not a profile. A permission set that holds only the users the system really needs to act for.
- Never an admin. No one with System Administrator or Modify All Data on the list.
- Guard the private key like the most important secret you have, because it is. Keep it in a vault, never in code.
- Short expiry and a JWT ID, so a stolen JWT is worth little.
When to use it
Section titled “When to use it”Only between systems you trust implicitly. In my experience, I’d only use JWT bearer when you really have control of both systems: say, two of a company’s own trusted servers, very secure, run by the same team. I’ve set it up at multiple clients, and it’s very useful when that’s true.
Here’s a typical one. Another company system pushes changes into Salesforce, and each change has to be recorded as the person who made it there, not as the integration user. Make sure the user exists in both systems, and the JWT names the right Salesforce user every time. Then the audit trail traces each change to a person.
You’ve probably used it already without noticing. The Salesforce CLI’s org login jwt command logs in with exactly this flow, using an external client app with the certificate you upload (S2). That’s how a build pipeline logs in to an org with nobody at the keyboard. This chapter’s practice does it by hand, so you can see every piece.
And if the other system won’t give Salesforce, or you, that much trust? That’s module 3.
Test knowledge. In my experience, know that JWT bearer is for a trusted server acting as a pre-authorized user, signed with the private key whose certificate is on the app, with no person and no refresh token.
Watch it on video
Section titled “Watch it on video”In 2023 I made a video of the JWT bearer flow, with the Burger House as an approved vendor and the sealed envelope. The note under the video says what’s still right and what’s changed since.
What’s next
Section titled “What’s next”Module 3: borrowed trust. The SAML bearer assertion and the SAML assertion flow.
What an architect decides here
Section titled “What an architect decides here”- Whether one system is trusted enough to act as people in another.
- Exactly who it may act as: a permission set with only those users, never a profile, never an admin.
- Where the private key lives, and who can reach it.
- The JWT’s lifetime, and whether to send a JWT ID.
Explore
Section titled “Explore”- OAuth 2.0 JWT Bearer Flow for Server-to-Server Integration (opens in a new tab) (Salesforce Help, Winter ‘27; checked 2026-11-07)
- Create an External Client App in Your Org (opens in a new tab) (Salesforce DX Developer Guide; checked 2026-11-07)
