Unattended CI/CD sandbox user authentication: access token generation
A fully automated CI/CD pipeline for Salesforce sandboxes usually has to authenticate API calls as specific, non-admin test users inside those sandboxes. The hard part is getting an OAuth access token for arbitrary users programmatically, with no manual step such as a browser redirect.
Here is where things stand, which paths I ruled out, and what might still work for pre-provisioned sandbox test users running unattended.
Current CI/CD automation status
The setup phases already run on their own:
- Sandbox provisioning is done.
- The Sandbox Administrator authenticates through the Tooling API (
/services/data/vXX.0/tooling/executeAnonymous) or an equivalent endpoint via the JWT Bearer flow, which yields an administrative access token. - The roughly 110 test users get created inside the sandbox.
The roadblock: arbitrary user token retrieval
What I need is a valid OAuth access token for each of those 110 test users, using only the admin token I already hold or static credentials I already have, such as the private key file.
Ruled out authentication flows
Several standard methods do not fit this unattended sandbox case:
sf org login webneeds an interactive browser flow, so unattended execution is out.sf org login jwtin its standard form leans on the sandbox's ownconsumerKey. That key changes with every new sandbox instance, so there is no reliable way to store it or fetch it programmatically for all test users across environments that keep getting rebuilt.- The
sandboxAuthTooling API endpoint is scoped to authenticate the sandbox creator, the admin user, rather than test users provisioned later. - The username/password OAuth flow is deprecated, and it still needs the Connected App's
consumerKeyandconsumerSecret, which vary the same way the JWT flow's do once they are tied to the sandbox instance.
Known technical assets
Here is what I can count on:
- A valid access token for the Sandbox Administrator.
- The usernames for every target test user.
- A static private key file that works across organizational environments.
- The Connected App configuration (Client ID/Consumer Key) is identical to Production, though the instance-specific Client ID still has to be resolved.
Investigating programmatic token generation paths
Direct flows are blocked by the changing consumerKey, so the answer probably lies in delegation or in reusing the session context I already have.
1. Admin-delegated token generation via REST
Can the administrative session I hold delegate authority and mint tokens for other users? That usually points at the OAuth 2.0 Token Exchange Flow.
The urn:ietf:params:oauth:grant-type:token-exchange grant lets a client holding one security token, here the admin access token, swap it for a new token on behalf of a different subject, here the test user. It depends on the Connected App permitting token exchange, and on the admin token presented being authorized to make that exchange for the subject you want.
So if the Connected App is configured for delegation or audience restriction, exchanging the admin token (subject_token) for a test user token (resource_owner_id) might be feasible.
2. Apex execution as target user
If no REST API exposes direct delegation, running Apex as the target user may supply the context instead.
- Use the existing Admin token to run an anonymous Apex block through the Tooling API (
/tooling/executeAnonymous). - The Apex runs under the Admin context.
- Call
System.runAs(TestUserRecord)to take on the test user's context inside that execution. - Inside the
runAsblock, try to reach or generate a session ID or token tied to that user. Producing a full OAuth token from inside Apex for external use is complex, and it often calls for custom Apex endpoints that mimic OAuth provider behavior, which may still want a Consumer Key.
3. Discovering sandbox-specific Connected App details
If I could fetch the consumerKey for the sandbox's copy of the Connected App programmatically, the standard JWT flow would work for each user.
That means checking the ConnectedApplication object, or related metadata through the Tooling API, to see whether the ConsumerKey for the sandbox instance can be queried after creation, perhaps keyed to the sandbox ID or the Admin ID.
// Pseudocode for potential Tooling API query
SELECT Id, ConsumerKey FROM ConnectedApplication WHERE Name = 'YourAppName'
If that key comes back, you can run the standard JWT flow for each test user with their username, the retrieved sandbox ConsumerKey, and the static Private Key.
Leave a Comment