Skip to main content
This guide describes how to configure Salesforce to accept OAuth 2.0 Token Exchange authentication from Superblocks. With this auth method, Superblocks performs a machine-to-machine token exchange to obtain a Salesforce access token on behalf of the current user without requiring the user to log in to Salesforce separately and without storing long-lived Salesforce credentials in Superblocks. Once configured, every Salesforce API call made through Superblocks will be executed as the specific user who is running the application, allowing Salesforce to enforce its existing record-level visibility rules, field-level security, and sharing settings.

Prerequisites

To follow this guide, you’ll need:
  • A Superblocks organization with enterprise SSO configured using an OIDC-based identity provider (for example, Okta OIDC or Microsoft Entra ID)
  • A Salesforce org (production or sandbox) with API access enabled
  • Salesforce System Administrator profile or a profile with the following permissions:
    • Manage External Client Apps
    • View Setup and Configuration
    • Customize Application
    • Author Apex

Token exchange flow

When using this auth method:
  1. Your user logs in to Superblocks using your enterprise SSO identity provider (for example, Okta)
  2. When the user runs a Superblocks API that includes a Salesforce step, a request is sent from the browser to the Superblocks Data Plane, including the user’s identity provider (IDP) JWT
  3. The Superblocks Agent extracts the user’s IDP access token and sends a token exchange request to Salesforce’s OAuth token endpoint using the urn:ietf:params:oauth:grant-type:token-exchange grant type as defined in RFC 8693
  4. Salesforce invokes the custom APEX Token Exchange Handler you deploy, which validates the IDP token, extracts the user’s email, and maps it to a Salesforce user
  5. Salesforce returns a scoped access token for that user
  6. The Superblocks Data Plane uses the access token to execute the Salesforce API call on behalf of the user

Setup instructions

Step 1: Create a Salesforce External Client App

An External Client App registers Superblocks as an OAuth client in your Salesforce org and enables the Token Exchange grant type.
  1. Log in to Salesforce and navigate to Setup
  2. In the Quick Find box, search for External Client Apps Manager
  3. Click New External Client App
  4. Fill in the Basic Information section:
    • External Client App Name: Superblocks
    • API Name: Superblocks (auto-populated)
    • Contact Email: your administrator email
  5. In the OAuth Settings section, check Enable OAuth Settings
  6. Set Callback URL to https://login.salesforce.com/services/oauth2/success
  7. Under OAuth Scopes, add the following scopes:
    • Manage user data via APIs (api)
    • Perform requests on your behalf at any time (refresh_token, offline_access) — optional, only needed if your use case requires offline access
  8. Scroll down to the OAuth 2.0 Token Exchange Flows section and check Enable Token Exchange Flow
  9. Click Save, then click Continue
After saving, Salesforce takes a few minutes to provision the app. Once available:
  1. From External Client App Manager, click the arrow next to your app and select Edit Settings
  2. Click the Settings tab, scroll down and open the OAuth Settings dropwdown
  3. Click on Consumer Key and Secret (you may need to verify via email)
  4. Copy and save the Consumer Key (Client ID) and Consumer Secret (Client Secret) — you will need these when configuring the Superblocks integration
  5. Go back to External Client App Manager, click the arrow next to your app and select Edit Policies
  6. Under OAuth PoliciesPermitted Users, select Admin approved users are pre-authorized

Step 2: Write the APEX Token Exchange Handler

Salesforce requires a custom APEX class that extends Auth.Oauth2TokenExchangeHandler. This handler is responsible for validating the incoming JWT and mapping it to a Salesforce user. When Salesforce receives a token exchange request, it invokes this handler, which is responsible for validating the incoming IDP access token and mapping the incoming identity provider token to the Salesforce user for whom Salesforce should issue the exchanged access token. The handler below decodes the IDP access token (a JWT), extracts the email claim, and looks up the matching active Salesforce user.

Deploy the handler class

  1. In Salesforce Setup, search for Developer Console and open it (or use the Salesforce CLI / VS Code Salesforce Extension)
  2. Go to FileNewApex Class
  3. Name the class SuperblocksTokenExchangeHandler
  4. Replace the default content with the following:
global class SuperblocksTokenExchangeHandler extends Auth.Oauth2TokenExchangeHandler {

    global override Auth.TokenValidationResult validateIncomingToken(
        String appDeveloperName,
        Auth.IntegratingAppType appType,
        String incomingToken,
        Auth.OAuth2TokenExchangeType tokenType
    ) {
        try {
            List<String> jwtParts = incomingToken.split('\\.');
            if (jwtParts.size() < 2) {
                return new Auth.TokenValidationResult(false);
            }

            String payload = jwtParts[1];
            Integer remainder = Math.mod(payload.length(), 4);
            if (remainder > 0) {
                payload = payload + '===='.substring(0, 4 - remainder);
            }

            payload = payload.replace('-', '+').replace('_', '/');

            String payloadJson = EncodingUtil.base64Decode(payload).toString();
            Map<String, Object> claims =
                (Map<String, Object>) JSON.deserializeUntyped(payloadJson);

            String identityValue = (String) claims.get('sub');
            if (String.isBlank(identityValue)) {
                identityValue = (String) claims.get('email');
            }
            if (String.isBlank(identityValue)) {
                identityValue = (String) claims.get('upn');
            }

            if (String.isBlank(identityValue)) {
                return new Auth.TokenValidationResult(false);
            }

            Auth.UserData userData = new Auth.UserData(
                (String) claims.get('sub'),
                null,
                null,
                null,
                identityValue,
                null,
                identityValue,
                null,
                null,
                null,
                null
            );

            return new Auth.TokenValidationResult(
                true,
                null,
                userData,
                incomingToken,
                tokenType,
                null
            );

        } catch (Exception e) {
            return new Auth.TokenValidationResult(false);
        }
    }

    global override User getUserForTokenSubject(
        Id networkId,
        Auth.TokenValidationResult result,
        Boolean canCreateUser,
        String appDeveloperName,
        Auth.IntegratingAppType appType
    ) {
        String identityValue = result.getUserData().email;

        List<User> users = [
            SELECT Id, Email, Username
            FROM User
            WHERE IsActive = true
            AND (Email = :identityValue OR Username = :identityValue)
            LIMIT 1
        ];

        if (!users.isEmpty()) {
            return users[0];
        }

        return null;
    }
}
  1. Click Save

Optional: validate the token signature

The example above decodes the JWT payload without verifying the signature. For production environments, you may want to add signature validation using your IDP’s public JWKS endpoint. Salesforce provides the Auth.JWTUtil class for this purpose, or you can make a callout to your IDP’s jwks_uri endpoint to retrieve the public key. If you implement signature validation, you will need to add your IDP’s JWKS endpoint to the Salesforce Remote Site Settings (Setup → Remote Site Settings → New Remote Site).

Step 3: Create and Configure the Token Exchange Handler

Salesforce requires a Token Exchange Handler record to link your Apex class to your External Client App.
  1. In Salesforce Setup, search for Token Exchange Handlers
  2. Click New
  3. Configure the following:
    • Name: Superblocks Token Exchange Handler
    • Apex Class: SuperblocksTokenExchangeHandler
    • Supported Token Types: JWT
  4. Save
  1. Open the Token Exchange Handler you just created
  2. Click Enable New App
  3. Select your External Client App
  4. Configure:
    • Run As User: Select a valid Salesforce user that the handler should run as. This user provides the execution context for the handler itself and must have sufficient permissions to perform user lookup and validation logic. The Salesforce user for whom the access token is issued is still determined dynamically by the handler based on the incoming token.
  5. Save

Step 4: Configure the Superblocks integration

Once your Salesforce External Client App and APEX handler are in place and the Token Exchange Handler is linked, configure the Salesforce integration in Superblocks.
  1. In Superblocks, navigate to the Integrations page
  2. Search for Salesforce and click New Integration (or edit an existing one)
  3. In the Authentication dropdown, select OAuth 2.0 Token Exchange
  4. Set Subject token source to Login Identity Provider
  5. Fill in the remaining fields using the values from your External Client App:
    Field
    RequiredDescription
    Token URLhttps://login.salesforce.com/services/oauth2/token for production orgs. Use https://test.salesforce.com/services/oauth2/token for sandbox orgs.
    Client IDThe Consumer Key from your External Client App
    Client secretThe Consumer Secret from your External Client App
    Scopeapi — required to make Salesforce REST API calls. Add additional scopes separated by spaces as needed.
  6. Click Test Connection to verify the configuration, then click Save

Testing & troubleshooting

Test the integration

With the integration configured, verify that it works end-to-end:
  1. In a Superblocks application, create a new Backend API
  2. Add a step that uses your Salesforce integration
  3. Add a SOQL query step, for example:
    SELECT Id, Name, Username FROM User WHERE Id = :$userId LIMIT 1
    
    Where $userId resolves to the current Salesforce user
  4. Click Run API
If everything is working, the step should return data for the Salesforce user that corresponds to the logged-in Superblocks user.

Common errors

Error message
Why it’s happeningResolution
Could not find identity provider tokenThe user is not logged in via an enterprise SSO connection, or the SSO connection is not configured to issue OIDC access tokensConfirm that your Superblocks SSO uses an OIDC-based flow. Contact support@superblocks.com if you need assistance.
Identity provider token expiredThe user’s IDP session has expiredAsk the user to refresh the browser or log out and back in to obtain a fresh IDP token
Could not find a user JWTThe integration is being used in a Workflow, Scheduled Job, or public App where there is no logged-in user contextToken Exchange requires an active user session. Use this integration type in Backend APIs only, not in Workflows or Scheduled Jobs.
Token exchange failedSuperblocks received an error from the Salesforce token endpointCheck the error details returned by Salesforce. Common causes: the External Client App is not yet provisioned (wait a few minutes after creation), the Token Exchange Flow is not enabled on the External Client App, or the handler class is not linked.
MISSING_EMAIL_CLAIM (from your APEX handler)The IDP access token does not include an email claimUpdate the SuperblocksTokenExchangeHandler to use the claim name your IDP includes. Check the IDP access token using jwt.io to confirm the correct claim.
USER_NOT_FOUND (from your APEX handler)No active Salesforce user was found with the email from the IDP tokenVerify that the user exists in Salesforce with an exactly matching email address (case-insensitive) and that their account is active.