Apex JWT Bearer Flow
The OAuth 2.0 JWT Bearer Token Flow lets server-side code obtain an access token without a stored password and without an interactive login. The client presents a short-lived JSON Web Token signed with a private key; the authorization server (Salesforce, or an external IdP) verifies the signature against a registered public key and returns an access token.
In Salesforce Apex this comes up in two shapes. The outbound shape
is the one this skill covers: Apex calling a non-Salesforce system
(another Salesforce org, a Heroku service, an internal API, a
SaaS that supports JWT bearer) and needing an access token. The
two correct ways to implement it are (a) a Named Credential with
"JWT Bearer" auth — Salesforce manages the assertion and token
exchange for you — and (b) Apex that builds the assertion with
Auth.JWT and Auth.JWS, posts it to /oauth2/token, and parses
the response.
The inbound shape — an external system getting a token into Salesforce via JWT — is configured entirely on the Connected App ("Use digital signatures" + uploaded certificate, "Pre-authorized" profile/permset). No Apex is needed. That is out of scope for this skill.
The mistakes are predictable. The JWT exp claim must be inside
the next ~5 minutes (Salesforce rejects assertions older than 5
minutes); aud must be the server's login URL (not your org's);
sub must be a username the Connected App has been pre-authorized
to issue tokens for; and the certificate used to sign must match
the public key uploaded to the Connected App. Any one of these
mismatches returns the same opaque invalid_grant error.
Recommended Workflow
- Prefer Named Credentials over hand-rolled Apex. Setup → Named
Credentials → New, choose "JWT Bearer" identity type. Salesforce
manages signing, refresh, and token caching. Reach for Apex
Auth.JWTonly when the target system requires a non-standard claim or a non-Salesforce IdP that Named Credentials does not support. - Generate or import the certificate in Setup → Certificate and
Key Management. Use a self-signed certificate for sandboxes;
in production use a CA-issued certificate if the target system
requires it. Note the label — you reference it from
Auth.JWS. - Configure the Connected App on the target side. Upload the
public key (the .crt exported from Setup), enable "Use digital
signatures", add the API scope you need, and pre-authorize the
user(s) the
subclaim will identify (Profiles → Manage Profiles or Permission Sets). - Write the assertion builder. Use
Auth.JWTfor the claims,Auth.JWSto sign with the certificate label. Setiss(consumer key),sub(username),aud(target login URL exactly —https://login.salesforce.comorhttps://test.salesforce.comfor Salesforce-to-Salesforce),expto now + 3 minutes. - POST the assertion to the token endpoint. Use form-encoded
body with
grant_type=urn:ietf:params:oauth:grant-type:jwt-bearerandassertion=<jwt>. Parse the response —access_tokenon success,{error, error_description}on failure. - Handle the failure responses, not just
invalid_grant.user hasn't approved this consumermeans pre-authorization is missing;invalid_assertionusually means clock skew or wrongaud;invalid_client_idmeans theissdoesn't match a Connected App. - Cache the access token until ~30s before expiry. The token endpoint is rate-limited (per-org, per-Connected-App). Re-exchange only on expiry or on a 401 from the resource server.
When To Reach For This Skill
Use it when a backend Apex job (Schedulable, Queueable, callout from a trigger) needs to call an external system as a fixed service account, and the target supports JWT bearer. Use it for org-to-org integration where one org calls the other unattended. Use it when storing a password (even encrypted) in Custom Metadata is unacceptable — JWT bearer has no password.
Do not use it for user-context callouts. If the calling user's permissions matter at the destination, use Named Credentials with "Per User" auth and the standard OAuth web-server flow. JWT bearer is impersonation by a service principal; it bypasses the user's interactive login.
What This Skill Does Not Cover
| Topic | See instead |
|---|---|
| Inbound JWT (external system → Salesforce) | security/connected-app-jwt-inbound (Connected App config; no Apex) |
| OAuth 2.0 user-agent / web-server flow | apex/named-credentials-oauth-user-flow |
| JWKS rotation and discovery endpoints | integration/jwt-key-rotation |
| MuleSoft / external orchestration of JWT | integration/mulesoft-jwt-passthrough |