Skip to main content
SFDC Developers
Integration

OAuth 2.0 JWT Bearer Flow: Packaging External Client Apps

Vinay Vernekar · · 4 min read

Introduction to Secure External Client App Distribution

For many Salesforce architects, the transition from Connected Apps to External Client Apps (ECAs) marks a significant evolution in how we manage third-party integrations. One of the most critical, yet frequently misunderstood, patterns is the use of the OAuth 2.0 JWT Bearer flow within a packaged application. A common pitfall is the assumption that security credentials—specifically private keys—should be bundled with your metadata. In this guide, we’ll dismantle that misconception and provide a blueprint for a secure, subscriber-centric implementation.

The Immutable Rule: Never Package Your Private Keys

Security in the Salesforce ecosystem relies on the principle of isolation. When you package an External Client App, you are distributing the structure of the integration, not the identity of the integration. If you embed a private key in your package, you are effectively hardcoding a single identity across every org that installs your app. This is a critical security vulnerability.

When a subscriber installs your External Client App, they must treat the app as a template. The subscriber is responsible for providing their own certificate and private key, ensuring that the identity of the integration remains unique to their environment. By following this pattern, you enforce the "Bring Your Own Credentials" (BYOC) model, which is the industry standard for secure enterprise distribution.

Designing for Subscriber-Specific Configuration

Since your package cannot contain the subscriber's private key, your code must be designed to fetch configuration dynamically. Avoid hardcoding Consumer Keys or certificate aliases in your Apex classes or integration service layers. Instead, leverage Custom Metadata Types or Custom Settings to store these values.

Let’s look at an example of an abstraction layer that allows the subscriber to input their details post-installation:

public with sharing class IntegrationConfigService {
    public static String getConsumerKey() {
        // Fetching from a Custom Setting allows the subscriber to input their key post-install
        Integration_Settings__c settings = Integration_Settings__c.getOrgDefaults();
        return settings.Consumer_Key__c;
    }

    public static String getCertificateAlias() {
        Integration_Settings__c settings = Integration_Settings__c.getOrgDefaults();
        return settings.Certificate_Alias__c;
    }
}

By using this pattern, the logic remains generic within your managed package, while the sensitive identifying information is isolated to the subscriber's org.

Implementing the JWT Bearer Flow in Your App

Once the subscriber has configured their own External Client App and uploaded their certificate, your Apex integration logic needs to generate the JWT. The Auth.JWTBearerTokenExchange class is your primary tool here. Ensure your code handles the token exchange by referencing the subscriber’s stored configuration.

Here is a practical implementation snippet for requesting an access token:

public class JWTFlowHandler {
    public String getAccessToken() {
        String consumerKey = IntegrationConfigService.getConsumerKey();
        String certName = IntegrationConfigService.getCertificateAlias();
        String tokenEndpoint = 'https://login.salesforce.com/services/oauth2/token';

        Auth.JWT jwt = new Auth.JWT();
        jwt.setAud('https://login.salesforce.com');
        jwt.setIss(consumerKey);
        jwt.setSub('[email protected]');
        jwt.setValidityLength(300);

        Auth.JWS jws = new Auth.JWS(jwt, certName);
        Auth.JWTBearerTokenExchange bearer = new Auth.JWTBearerTokenExchange(tokenEndpoint, jws);
        
        return bearer.getAccessToken();
    }
}

Deployment Best Practices for Subscribers

To ensure a smooth experience for your users, provide clear documentation on how to perform the post-installation setup. Your documentation should include these steps:

  1. Certificate Generation: Instruct subscribers to generate a self-signed or CA-signed certificate within their own environment.
  2. External Client App Setup: Direct users to the "External Client Apps" menu to define their specific OAuth settings, using their generated Consumer Key and Certificate.
  3. Post-Install Setup UI: If possible, create a simple Lightning Web Component (LWC) that allows admins to save their Consumer Key and Certificate Alias into your custom configuration object, simplifying the entry process.
  4. Permission Sets: Ensure your package includes a Permission Set that grants access to the specific Apex classes and Custom Metadata types required to perform the OAuth dance.

Key Takeaways

  • Security Boundary: Your package defines the integration logic, but the subscriber's org defines the security identity. Never include private keys in your source control or package.
  • Abstraction is Key: Use Custom Metadata Types to allow subscribers to inject their unique Consumer Keys and Certificate aliases after installation.
  • Dynamic Discovery: Build your integration service layers to fetch security configurations dynamically rather than relying on hardcoded constants.
  • Subscriber Empowerment: By following the BYOC model, you ensure that even if one subscriber's security is compromised, the rest of your user base remains isolated and safe.
  • Leverage Native Tools: Use Salesforce's Auth.JWTBearerTokenExchange class to standardize your token generation, keeping the implementation compliant with Salesforce's internal security standards.

Share this article

Vinay Vernekar

Vinay Vernekar

Salesforce Developer & Founder

Vinay is a seasoned Salesforce developer with over a decade of experience building enterprise solutions on the Salesforce platform. He founded SFDCDevelopers.com to share practical tutorials, best practices, and career guidance with the global Salesforce community.

Get weekly Salesforce dev tutorials in your inbox

Comments

Loading comments...

Leave a Comment

Trending Now