(Salesforce inbound – Salesforce is server and External System is Client)
JWT Bearer Flow allows one system to log into another system without username/password. Instead, it uses:
- JWT (JSON Web Token) -> a signed secure token
- Certificate (Private Key) -> to prove identity
Sequence
- First, External system will create certificate (key pair = Private Key, Public Key[Put inside certificate (.crt)]). Key Pair (always comes together).
When someone says “certificate” casually, they usually mean a key pair, which has “Private Key (Secret)” and “Public Key (Shared)”. A certificate (.crt/.cer) is basically: Public Key + Identity Information + Signature.
What Exactly Private Key Does?
- Creates a hash of data
- Encrypts hash using RSA
- Produces signature
- Only matching public key can verify this
- External system team will share that public certificate with Salesforce team.
- Salesforce Upload public certificate in Setup -> Certificate & Key Management.
- Salesforce Creates Connected App.
- Enable JWT OAuth flow.
- Upload above same certificate (Shared by External System Team) in Connected App.
- Add scopes (api, refresh_token)
- External System Creates JWT: JWT will contain:
- External System Create Header
{
"alg": "RS256",
"typ": "JWT"
}
RS256 = RSA + SHA256 algorithm
- External System Create Payload: Contains: username, expiry, audience
“iss” = issuer = Client ID (Shared by Salesforce)
“sub” = username = Integration User (Shared by Salesforce)
“aud” = login url = “https://login.salesforce.com”
“exp” = expiration time
{
"iss": "CLIENT_ID",
"sub": "[email protected]",
"aud": "https://login.salesforce.com",
"exp": 1712345678
}
- External System then, Encode Header & Payload:
Base64UrlEncode(header)
Base64UrlEncode(payload)
Then join: header.payload
- External System SIGN using Private Key. This produces a digital signature.
Signing = creating a secure digital signature on JWT using Private Key.
This ensures, Token is tamper-proof and Sender identity is verified
This is the main part:
signature = Sign( data = "header.payload", private_key, algorithm = RS256 )
Below example(Java)
String jwt = Jwts.builder()
.setIssuer("CLIENT_ID")
.setSubject("[email protected]")
.setAudience("https://login.salesforce.com")
.setExpiration(expDate)
.signWith(privateKey, SignatureAlgorithm.RS256)
.compact();
- External System prepares Final JWT
JWT = header.payload.signature
JWT Structure – JWT has 3 parts:HEADER.PAYLOAD.SIGNATURE
eyJhbGciOiJSUzI1NiJ9. eyJpc3MiOiJDTElFTlRfSUQiLCJzdWIiOiJ1c2VyIn0. abc123signature
- External System Sends JWT to Salesforce
POST /services/oauth2/token grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer assertion=<signed JWT>
- Salesforce Takes header.payload and Takes signature
- Salesforce picks certificate from Connected App.
- Salesforce Extracts public key in Certificate.
- Salesforce Verifies: Is this signature generated by matching private key? If YES -> trusted
- Salesforce sends access_token and instance_url to External System back.
- Next, External System uses that “access token” in Authorization Header (Bearer <access_token>)
- Lastly, External System make Salesforce API calls.
Important Technical Points
- Technical
- Algorithm: Always: RS256 (RSA + SHA256)
- Encoding: Uses Base64URL, NOT normal Base64
- Expiry: Must be short-lived (e.g., 3–5 minutes)
- External system signs JWT using Private Key via RS256 -> Salesforce verifies using Public Key
- Signing = hashing + encrypting with private key to produce a signature that only the public key can validate
Common Mistakes (Helps in Debugging)
| Issue | Reason |
| invalid signature | Wrong private key |
| invalid assertion | Incorrect signing |
| wrong algorithm | Not using RS256 |
| audience mismatch | Wrong aud field |
| expired token | exp too old |