What this covers
- Define a dedicated automation identity and the minimum access it needs.
- Generate a private key and public certificate without embedding passphrases in shell history.
- Configure an External Client App, authenticate Salesforce CLI and plan key rotation.
The OAuth 2.0 JWT bearer flow is designed for non-interactive authentication. A CI/CD runner signs an assertion with a private RSA key, Salesforce verifies the signature against a public certificate registered for the app, and the runner receives an access token for a named Salesforce user.
JWT removes the browser step; it does not remove the need for identity, access and secret management. Anyone who can use the private key, client ID and approved username may be able to authenticate with that user's Salesforce permissions.
Choose the automation identity first
- Use a dedicated integration user rather than a developer or administrator account.
- Grant only the object, metadata, test and deployment access required by the pipeline.
- Keep production and non-production identities, app policies and secrets separate.
- Record an owner for the user, app, certificate and rotation process.
- Make login, validation and deployment output part of the pull-request or release evidence.
Generate a key and certificate
The following OpenSSL commands generate a 2048-bit RSA private key and a one-year self-signed X.509 certificate. Run them in a restricted working directory on a trusted machine. The private key stays secret; only the certificate is uploaded to Salesforce.
umask 077
openssl genpkey -algorithm RSA \
-pkeyopt rsa_keygen_bits:2048 \
-out salesforce-ci.key
openssl req -new -x509 -sha256 -days 365 \
-key salesforce-ci.key \
-subj "/CN=salesforce-ci" \
-out salesforce-ci.crt
chmod 600 salesforce-ci.key
Configure the External Client App
Salesforce labels and policy screens can differ by release, so use the current Salesforce setup guidance alongside these steps.
- In Setup, open External Client App Manager and create a new External Client App.
- Enable OAuth and the JWT bearer flow, then upload salesforce-ci.crt as the public certificate.
- Select only the OAuth scopes the automation needs. Avoid Full access when API access is sufficient.
- Set the app policy to require administrator-approved users.
- Authorise the dedicated integration user through the profile or permission-set policy supported by the app.
- Copy the consumer key, which Salesforce CLI uses as the client ID. The JWT bearer flow does not require the consumer secret for the CLI command shown below.
Use the organisation's My Domain or the correct Salesforce login endpoint for the target. A sandbox URL, production login URL and My Domain are not interchangeable in every security configuration, so store the expected instance URL as an explicit pipeline variable.
Store the CI values separately
- SF_CLIENT_ID: the External Client App consumer key.
- SF_USERNAME: the dedicated Salesforce integration username.
- SF_INSTANCE_URL: the approved login or My Domain URL for that org.
- SF_JWT_KEY: the complete PEM-encoded private key, held as an encrypted secret or secret file.
Keep these values scoped to the environment that needs them. In particular, do not expose production secrets to untrusted pull-request jobs or forked repositories.
Authenticate Salesforce CLI
Use the CI provider's secret-file mechanism to write the PEM key into a temporary file without printing it. Then authenticate with explicit values and a local alias. Avoid setting a global default org in a shared runner.
sf org login jwt \
--client-id "$SF_CLIENT_ID" \
--jwt-key-file "$RUNNER_TEMP/salesforce-ci.key" \
--username "$SF_USERNAME" \
--instance-url "$SF_INSTANCE_URL" \
--alias ci-target
sf org display --target-org ci-target
Diagnose failures by layer
- App policy: confirm the integration user is active and approved for the External Client App.
- Certificate: confirm the uploaded certificate matches the private key available to the runner.
- Endpoint: confirm the instance URL is for the intended production org or sandbox.
- Time: check runner clock synchronisation because JWT assertions are time-bound.
- Permissions: distinguish successful authentication from a later deployment or API authorisation failure.
- Propagation: allow for Salesforce app and policy changes to take effect before treating the configuration as broken.
Plan rotation and recovery
Record the certificate expiry date and rotate well before it. Test the replacement key and certificate through the same pipeline path before retiring the old material, following the app capabilities and security policy available in the org. Revoke the automation user or app access immediately if the private key may have been exposed.
A working login is only the first control. Production readiness also requires an explicitly targeted pipeline, a least-privilege user, protected key material, reviewable deployment actions and a tested recovery route.
Official references
- Salesforce CLI JWT login command — Salesforce Developers
- Set up an External Client App for JWT bearer authentication — Salesforce Developers
- OpenSSL req command reference — OpenSSL Documentation
