This guide explains how to authenticate against the OrderCentral API using Salesforce’s JWT Bearer OAuth Flow through a configured External Client App.
This flow is designed for secure server-to-server integrations with no user interaction required.
What You Receive from the OrderCentral Admin
Before you begin, the Salesforce admin must provide you with:
- Client ID (from the External Client App)
- Salesforce OAuth Domain, such as:
https://login.salesforce.com(production)https://test.salesforce.com(sandbox)- or a My Domain URL (e.g.
https://mydomain.my.salesforce.com)
- Salesforce Username that your integration should impersonate
- OAuth scopes that are allowed (at minimum:
api) - The public certificate (optional)
You must have:
- The private key matching the certificate the admin uploaded. This is used to sign JWTs with RS256.
Flow Overview
Your system will:
- Create a JWT containing required Salesforce claims.
- Sign the JWT using RS256 and the private key.
- POST the signed JWT to Salesforce at
/services/oauth2/token. - Receive an access token and instance URL.
- Call the OrderCentral API with the access token.
- Repeat when the token expires (no refresh token is used in this flow).
Constructing the JWT
A valid JWT has a header, payload, and signature.
Header
{
"alg": "RS256"
}
Payload Claims
The JWT payload must contain:
| Claim | Description |
|---|---|
iss |
Client ID of the External Client App |
sub |
Salesforce username to impersonate |
aud |
Salesforce OAuth domain (the login host) |
exp |
Expiration time (Unix timestamp). Usually now + 3 to 5 minutes |
Example:
{
"iss": "3MVG9...ClientId",
"sub": "[email protected]",
"aud": "https://login.salesforce.com",
"exp": 1733839800
}
Signing the JWT
Sign the JWT using RS256 with your private key.
The resulting string must be formatted as:
{Base64UrlHeader}.{Base64UrlPayload}.{Base64UrlSignature}
Requesting an Access Token from Salesforce from Salesforce
Endpoint
POST {OAUTH_DOMAIN}/services/oauth2/token
Where {OAUTH_DOMAIN} is the domain the admin provided.
Request Body
Send the following as URL-encoded form data:
grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer
assertion={YOUR_SIGNED_JWT}
Example Curl Request
curl -X POST "https://login.salesforce.com/services/oauth2/token" \
-d "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer" \
-d "assertion=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
Example Response
{
"access_token": "00Dxx0000001gERT!...",
"scope": "api",
"instance_url": "https://mydomain.my.salesforce.com",
"id": "https://login.salesforce.com/id/00Dxx0000001gERT/005xx000001Sv6V",
"token_type": "Bearer",
"issued_at": "1733837468925",
"signature": "wxyz..."
}
Key Values You Need
- access_token: used for API calls
- instance_url: base URL for OrderCentral API endpoints
Calling the OrderCentral API
Use the access_token and instance_url from the token response.
All calls must include:
Authorization: Bearer {access_token}
Content-Type: application/json
Example
curl -X GET \
"{instance_url}/services/apexrest/ordercentral/v1/orders" \
-H "Authorization: Bearer 00Dxx0000..." \
-H "Content-Type: application/json"
Replace /ordercentral/v1/orders with the correct path for your project.
Token Expiration Handling
The JWT flow does not return a refresh token. When an access token expires:
- Generate a new JWT with a fresh
expclaim. - Sign it again using RS256.
- POST it to Salesforce using the same endpoint.
- Receive a new access token.
This process can be repeated indefinitely.
Example Implementation (Node.js) (Node.js)
import fs from "fs";
import * as jose from "jose";
const clientId = process.env.SF_CLIENT_ID;
const username = process.env.SF_USERNAME;
const authDomain = process.env.SF_AUTH_DOMAIN; // e.g. https://login.salesforce.com
const privateKeyPem = fs.readFileSync("./private.key", "utf8");
async function getAccessToken() {
const now = Math.floor(Date.now() / 1000);
const payload = {
iss: clientId,
sub: username,
aud: authDomain,
exp: now + 3 * 60
};
const privateKey = await jose.importPKCS8(privateKeyPem, "RS256");
const jwt = await new jose.SignJWT(payload)
.setProtectedHeader({ alg: "RS256" })
.sign(privateKey);
const params = new URLSearchParams();
params.append("grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer");
params.append("assertion", jwt);
const response = await fetch(`${authDomain}/services/oauth2/token`, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: params.toString()
});
if (!response.ok) {
throw new Error(await response.text());
}
const data = await response.json();
return {
accessToken: data.access_token,
instanceUrl: data.instance_url
};
}
Common Errors
- invalid_grant: User not approved, wrong
sub, wrongaud, or expiredexp. - unsupported_grant_type: Ensure the grant type string is correct.
- invalid_client_id: Using the wrong Client ID or environment.
Summary
To authenticate using the External Client App via JWT Bearer:
- Build a JWT containing
iss,sub,aud, andexp. - Sign it with RS256 using your private key.
- Exchange it for an access token using Salesforce's
/tokenendpoint. - Use the token to call OrderCentral REST endpoints.
- Regenerate tokens as needed.
You now have everything required to integrate securely with the OrderCentral API.