How to securely integrate Salesforce CRM endpoints into WordPress custom plugins using REST API Controllers
Securing Salesforce REST API Access in WordPress
Integrating Salesforce CRM data into a WordPress site via its REST API offers powerful opportunities for dynamic content and streamlined workflows. However, direct, unauthenticated access is a significant security risk. This guide details how to implement secure authentication and authorization mechanisms within a custom WordPress plugin to interact with Salesforce endpoints, focusing on OAuth 2.0 and best practices for managing credentials.
Prerequisites: Salesforce Connected App Configuration
Before writing any WordPress code, a Salesforce Connected App must be configured to enable API access. This involves obtaining a Consumer Key and Consumer Secret, which will be used for OAuth authentication.
- Navigate to Setup in Salesforce.
- Under Apps, select “App Manager”.
- Click “New Connected App”.
- Fill in the App Name, API Name, and Contact Email.
- Enable OAuth Settings.
- Set the Callback URL to your WordPress site’s endpoint that will handle the OAuth redirect (e.g.,
https://your-wordpress-site.com/wp-admin/admin-ajax.php?action=salesforce_oauth_callback). - Select the appropriate OAuth Scopes (e.g., “Access and manage your data (api)”).
- Save the Connected App.
- Once saved, locate the Connected App in the App Manager and click “View” to retrieve the Consumer Key and Consumer Secret. Keep these confidential.
WordPress Plugin Structure and Initialization
We’ll create a basic WordPress plugin structure. The core logic for authentication and API calls will reside within a custom class. We’ll leverage WordPress’s AJAX API for handling redirects and callbacks.
Plugin File Structure
Create a directory for your plugin (e.g., salesforce-integration) within wp-content/plugins/. Inside this directory, create the main plugin file (e.g., salesforce-integration.php).
Main Plugin File: salesforce-integration.php
This file will contain the plugin header and instantiate our main plugin class.
<?php
/**
* Plugin Name: Salesforce Integration
* Description: Securely integrates Salesforce REST API endpoints into WordPress.
* Version: 1.0.0
* Author: Your Name
* Author URI: https://your-website.com
* License: GPL-2.0+
* License URI: http://www.gnu.org/licenses/gpl-2.0.txt
* Text Domain: salesforce-integration
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
// Define constants for Salesforce credentials and endpoints.
// It's highly recommended to store these in wp-config.php or a secure options table.
define( 'SALESFORCE_CONSUMER_KEY', 'YOUR_SALESFORCE_CONSUMER_KEY' );
define( 'SALESFORCE_CONSUMER_SECRET', 'YOUR_SALESFORCE_CONSUMER_SECRET' );
define( 'SALESFORCE_REDIRECT_URI', admin_url( 'admin-ajax.php?action=salesforce_oauth_callback' ) );
define( 'SALESFORCE_AUTH_ENDPOINT', 'https://login.salesforce.com/services/oauth2/authorize' );
define( 'SALESFORCE_TOKEN_ENDPOINT', 'https://login.salesforce.com/services/oauth2/token' );
define( 'SALESFORCE_API_BASE_URL', 'https://your-domain.my.salesforce.com/services/data/v58.0/' ); // Replace with your Salesforce domain and API version
// Include the main plugin class.
require_once plugin_dir_path( __FILE__ ) . 'includes/class-salesforce-integration.php';
/**
* Begins execution of the plugin.
*/
function run_salesforce_integration() {
$plugin = new Salesforce_Integration();
$plugin->run();
}
run_salesforce_integration();
?>
Implementing the Salesforce Integration Class
Create a directory named includes within your plugin directory. Inside includes, create class-salesforce-integration.php.
includes/class-salesforce-integration.php
JavaScript for Admin Interface and AJAX Calls
Create a directory named js within your plugin directory. Inside js, create salesforce-admin.js.
js/salesforce-admin.js
jQuery(document).ready(function($) {
// Handle Salesforce Authentication Button Click
$('#authenticate-salesforce').on('click', function(e) {
e.preventDefault();
var button = $(this);
button.text('Redirecting...');
button.prop('disabled', true);
$.ajax({
url: salesforce_ajax_object.ajax_url,
type: 'POST',
data: {
action: 'initiate_salesforce_auth'
},
success: function(response) {
if (response.success) {
window.location.href = response.data.redirect_url;
} else {
alert('Error initiating authentication: ' + response.data.message);
button.text('Authenticate with Salesforce');
button.prop('disabled', false);
}
},
error: function(jqXHR, textStatus, errorThrown) {
alert('AJAX Error: ' + textStatus + ' - ' + errorThrown);
button.text('Authenticate with Salesforce');
button.prop('disabled', false);
}
});
});
// Handle Refresh Token Button Click
$('#refresh-salesforce-token').on('click', function(e) {
e.preventDefault();
var button = $(this);
var originalText = button.text();
button.text('Refreshing...');
button.prop('disabled', true);
$.ajax({
url: salesforce_ajax_object.ajax_url,
type: 'POST',
data: {
action: 'refresh_salesforce_token' // This action needs to be added to the PHP class
},
success: function(response) {
if (response.success) {
$('#salesforce_status_message').html('<div class="notice notice-success is-dismissible"><p>' + response.data.message + '</p></div>');
} else {
$('#salesforce_status_message').html('<div class="notice notice-error is-dismissible"><p>Error: ' + response.data.message + '</p></div>');
}
button.text(originalText);
button.prop('disabled', false);
},
error: function(jqXHR, textStatus, errorThrown) {
$('#salesforce_status_message').html('<div class="notice notice-error is-dismissible"><p>AJAX Error: ' + textStatus + ' - ' + errorThrown + '</p></div>');
button.text(originalText);
button.prop('disabled', false);
}
});
});
// Handle Disconnect Button Click
$('#disconnect-salesforce').on('click', function(e) {
e.preventDefault();
if (!confirm('Are you sure you want to disconnect Salesforce?')) {
return;
}
var button = $(this);
var originalText = button.text();
button.text('Disconnecting...');
button.prop('disabled', true);
$.ajax({
url: salesforce_ajax_object.ajax_url,
type: 'POST',
data: {
action: 'disconnect_salesforce' // This action needs to be added to the PHP class
},
success: function(response) {
if (response.success) {
$('#salesforce_status_message').html('<div class="notice notice-success is-dismissible"><p>' + response.data.message + '</p></div>');
// Optionally redirect or reload the page
location.reload();
} else {
$('#salesforce_status_message').html('<div class="notice notice-error is-dismissible"><p>Error: ' + response.data.message + '</p></div>');
button.text(originalText);
button.prop('disabled', false);
}
},
error: function(jqXHR, textStatus, errorThrown) {
$('#salesforce_status_message').html('<div class="notice notice-error is-dismissible"><p>AJAX Error: ' + textStatus + ' - ' + errorThrown + '</p></div>');
button.text(originalText);
button.prop('disabled', false);
}
});
});
// Handle Test API Call Button Click
$('#test-salesforce-api').on('click', function(e) {
e.preventDefault();
var button = $(this);
var originalText = button.text();
button.text('Fetching...');
button.prop('disabled', true);
$('#api_response').text('Loading...');
$.ajax({
url: salesforce_ajax_object.ajax_url,
type: 'POST',
data: {
action: 'test_salesforce_api_call' // This action needs to be added to the PHP class
},
success: function(response) {
if (response.success) {
$('#api_response').text(JSON.stringify(response.data, null, 2));
} else {
$('#api_response').text('Error: ' + response.data.message);
}
button.text(originalText);
button.prop('disabled', false);
},
error: function(jqXHR, textStatus, errorThrown) {
$('#api_response').text('AJAX Error: ' + textStatus + ' - ' + errorThrown);
button.text(originalText);
button.prop('disabled', false);
}
});
});
});
Adding AJAX Actions for Refresh, Disconnect, and Test Call
We need to add the refresh_salesforce_token, disconnect_salesforce, and a test API call handler to our Salesforce_Integration class.
First, add the following methods to the Salesforce_Integration class in includes/class-salesforce-integration.php:
// ... (previous methods in the class) ...
/**
* Handles the AJAX request to refresh the Salesforce token.
*/
public function handle_refresh_salesforce_token() {
// Ensure this is called via AJAX and user has permissions
check_ajax_referer( 'salesforce_integration_nonce', 'security' ); // Add nonce for security
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( array( 'message' => __( 'Permission denied.', $this->plugin_name ) ) );
}
$this->refresh_salesforce_token(); // Call the existing refresh method
// The refresh_salesforce_token method already handles sending JSON response on error.
// If it succeeds, it updates options and returns true. We need to send a success response.
if ( get_option( 'salesforce_access_token' ) ) {
wp_send_json_success( array( 'message' => __( 'Salesforce token refreshed successfully!', $this->plugin_name ) ) );
} else {
wp_send_json_