Connecting One Drupal Site to Multiple Salesforce Orgs

Salesforce logo, a blue cloud with white type, in front of multiple clouds overlapping in the background
Behind the Scenes

The Project

The California Electric Vehicle Infrastructure Project (CALeVIP) offers incentives for the purchase and installation of electric vehicle charging infrastructure at publicly accessible sites throughout California. The California Energy Commission connects a national network of site hosts, equipment providers, and regional program administrators to deploy and maintain charging infrastructure throughout the state. Drupal provides the beautiful, powerful, and flexible content management tools they depend on. And Salesforce provides robust reporting, data management, and workflow tools to manage their application pipeline. Decentralization of the program has helped them scale up quickly.

The Challenge

Regional programs are tailored to the needs of their administrators and the constraints of enabling legislation. Each one has its own unique requirements, as well as its own Salesforce org. Salesforce administrators thus have flexibility to tweak their orgs as needed. But on the Drupal side, applicants want a consolidated dashboard where they can manage all their projects in one place. How can we connect one Drupal site with all these different Salesforce orgs? And how can we do it in a way that's maintainable, scalable, and eventually self-service for Drupal administrators?

The Solution

Salesforce Suite, developed and maintained by Message Agency, provides a host of powerful features out of the box. The authentication, mapping UI, and synchronization tools can get a complex Drupal-Salesforce integration project up and running with minimal custom programming. So what does an integration look like across multiple Salesforce orgs? While the Salesforce Suite supports one connection out of the box, its extensibility allows us to inject our own credentials to make it happen.

Say what now?

Here's how it works from a high level:

  • Define the credentials for our OAuth connections using yaml configuration
  • Using a custom Service Provider, we override the salesforce.client RestClient service to add additional arguments for those credentials
  • Override RestClient getters to return our own credentials

That's it?

I left out a key component from this list, which is our implementation of a consumer for Salesforce's JWT OAuth server-to-server authentication scheme. Using shared authentication certificates, our JWT consumer can authenticate directly to Salesforce without any human interaction. This key piece of the puzzle finally resolves the burden of juggling credentials between environments and manually re-authenticating after re-synching our content. This has since been implemented directly into the suite since this project as completed.

What does it look like in practice?

This structure makes it simple to get a client with the correct authentication. While we often use these in constructors in practice it can be called arbitrarily with three lines.

$client = \Drupal::service('salesforce.client');
$auth = \Drupal::service('example_instance.auth_token');
$client->setAuthToken($auth);

Changing which instance you're interacting with is easy as changing the AuthToken. For each of CALeVIP's regional initiatives, we create a new example_instance.auth_token service to connect the project's Salesforce org.

What are the advantages of authentication as a service?

Implementing authentication as its own service means that we can utilize any number of disparate Salesforce instances just by instantiating a new REST client. Separating the concern of authentication from other APIs leads to a simpler interface for developers, allowing us to focus on data APIs.

Looking to the future, this architecture allows us to give more control to the site administrators and builders. By having a dedicated authentication service we can create a configuration form where site admins can get a new project up and running quickly and efficiently.