1. Authentication and security

Single sign-on

Single Sign-On (SSO) enables users to authenticate with the OrderCloud API using trusted identity providers. This allows shoppers to use existing accounts (like Google or Facebook) or corporate credentials for authentication.

Implementing SSO with OpenID Connect

OpenID Connect protocol enables single sign-on through RESTful API exchanges between an Identity Provider (IDP) and a Relying Party (RP). This guide focuses on configuring OrderCloud to trust an IDP for user authentication.

Configuring your identity provider (IDP)

OrderCloud requires four pieces of information from your IDP, whether using a service like Google or a custom implementation:

PropertiesDescription
TokenEndpointURL for obtaining user-specific JSON web token
AuthorizationEndpointURL for user IDP credential entry
ConnectClientIDRequired for URL access
ConnectClientSecretRequired for URL access

API Reference: Create an OpenID Connect Configuration

Keep Client ID and Client Secret private. URLs can be public (example: Google's URLS). Your IDP needs OrderCloud's Authorized Redirect URI:

https://sandboxapi.ordercloud.io/ocrpcode

This endpoint converts IDP access tokens to OrderCloud tokens and redirects users to your specified AppStartUrl.

Configuring OrderCloud (RP)

Step 1: Create an OpenID Connect integration event

Set up an OpenID Connect Integration Event with two hosted endpoints in your middleware application. See "Configuring Your Integration Event Code" section for details.

API Reference: Create an Integration Event

Step 2: Create connection configuration

Each configuration connects users to one OrderCloud ApiClient through one IDP:

API Reference: Create an OpenID Connect Configuration

http
POST sandboxapi.ordercloud.io/v1/openidconnects HTTP/1.1
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...
Content-Type: application/json
json
{
  "ID": "anormalordercloudid",
  "OrdercloudApiClient": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "ConnectClientID": "...",
  "ConnectClientSecret": "...",
  "AppStartUrl": "https://my-buyer-application.com/login?token={0}",
  "AuthorizationEndpoint": "...",
  "TokenEndpoint": "...",
  "IntegrationEventID": "integrationeventID",
  "CustomErrorUrl": "https://my-buyer-application.com/error?ErrorMessage={0}"
}

Test the configuration as IDP fields are validated during login. The AppStartUrl redirects to your front-end app after login, with {0} replaced by a valid OrderCloud token.

Implementing login

Direct users to:

https://sandboxapi.ordercloud.io/ocrplogin?id=<ID>&roles=<Roles>&cid=<ApiClientID>

Parameters:

  • <ID>: Configuration ID
  • <ApiClientID>: OrdercloudAPIClient field
  • <Roles>: Space-separated list of requested roles (standard and custom roles supported)

Users enter IDP credentials and receive an OrderCloud token via the AppStartUrl. Your application must handle token storage.

Configuring your integration event code

Host two endpoints for the OpenID Connect Integration Event:

/createuser

Called on first-time user authentication:

Request body:

json
{
  "ExistingUser": null,
  "OpenIdConnect": {},
  "TokenResponse": {
    "id_token": "eyJhbGciOiJSU...",
    "access_token": "ya29.a0ARrdaM92vGZsFj"
  },
  "Environment": "Sandbox",
  "OrderCloudAccessToken": "eyJhbGciOiJSU...",
  "ConfigData": null
}

Required response after user creation:

json
{
  "Username": "neworderclouduser",
  "ErrorMessage": null
}

/syncuser

Optional endpoint for subsequent authentications (requires CallSyncUserIntegrationEvent: true):

Request body:

json
{
  "ExistingUser": {},
  "OpenIdConnect": {},
  "TokenResponse": {
    "id_token": "eyJhbGciOiJSU...",
    "access_token": "ya29.a0ARrdaM92vGZsFj"
  },
  "Environment": "Sandbox",
  "OrderCloudAccessToken": "eyJhbGciOiJSU...",
  "ConfigData": null
}

Required response after user update:

json
{
  "ErrorMessage": null
}

Setting ErrorMessage in any response terminates the process.

Deep linking

Enable specific page redirects after login using the appstartpath parameter:

https://sandboxapi.ordercloud.io/ocrplogin?id=<ID>&roles=<Roles>&cid=<ApiClientID>&appstartpath=/products/my-promotional-product

Configure AppStartUrl with {2} placeholder:

https://my-buyer-application.com{2}?token={0}

Example URL-encoded path:

https://sandboxapi.ordercloud.io/ocrplogin?id=<ID>&roles=<Roles>&cid=<ApiClientID>&appstartpath=%2Fproducts%2Fmyawesomeproduct

Final redirect:

https://my-buyer-application.com/products/myawesomeproduct?token={yourordercloudtoken}

IDP token

Access IDP tokens using {1} in AppStartUrl:

https://my-buyer-application.com?token={0}&idptoken={1}

For Azure, include Application ID in OpenIdConnect.AdditionalIdpScopes.

OrderCloud refresh token

Include refresh tokens using {3} in AppStartUrl:

https://my-buyer-application.com?token={0}&refresh={3}

Requires API client configuration with refresh token duration.

Custom error redirect

Configure error handling with CustomErrorUrl:

https://my-buyer-application.com/error?ErrorMessage={0}

Custom query parameters to AuthorizationEndpoint

Add custom parameters using customParams:

https://sandboxapi.ordercloud.io/ocrplogin?id=<ID>&roles=<Roles>&cid=<ApiClientID>&appstartpath=%2Fproducts%2Fmyawesomeproduct&customParams=locale%3Dus

If you're using Azure B2C as your IDP, you'll need to include customParams=response_mode%3Dform_post in your GET /ocrplogin request. This will ensure Azure sends OrderCloud a POST request containing the authorization code in the request body rather than a GET request with the authorization code in the query parameters. Using any other response_mode is not recommended for those using Azure B2C as their IDP due to the excessive length of authorization codes generated by Azure.

Implementing SSO with Azure AD B2C IDP

OpenID Connect is a powerful feature that enables you to provide single sign-on capabilities for any identity provider that supports the specification. In this tutorial we'll walk you step by step through what you'll need to get single sign-on working by using Azure AD B2C as the identity provider. By the end of this tutorial, you'll be able to sign in via Azure and be logged into OrderCloud.

Implementation outcome

Before we start, let's understand the finished product. By the end of this tutorial you will have a locally running application that will redirect you to Azure's sign-in page and after successfully signing in you should see your login details including:

  • Currently authenticated user
  • OrderCloud Access Token
  • OrderCloud Refresh Token (if configured)
  • Azure ID Token (if configured)

Marketplace configuration

First, you need to access your Marketplace in the Sitecore Cloud Portal.

After accessing, take special note of the OrderCloud Base API URL, which identifies the base URL needed for all API requests.

For this demonstration we are on the Sandbox environment in the region Us-West so our base API URL is https://sandboxapi.ordercloud.io, yours may look different.

Supporting entities

We will be creating a single-sign-on experience for buyer users specifically, so we'll create the most basic OrderCloud entities required to support that scenario.

Create a buyer organization

http
POST sandboxapi.ordercloud.io/v1/buyers HTTP/1.1
Authorization: Bearer INSERT_ACCESS_TOKEN_HERE
Content-Type: application/json; charset=UTF-8

{
  "ID": "buyer1",
  "Name": "Buyer 1",
  "Active": true
}
javascript
import { Tokens, Buyers } from "ordercloud-javascript-sdk";

Tokens.Set("INSERT_ACCESS_TOKEN_HERE");
Buyers.Create({
  ID: "buyer1",
  Name: "Buyer 1",
  Active: true
})
.then(response => {
  console.log(response);
})
.catch(err => console.log(err));
typescript
import { Tokens, Buyers, Buyer, OrderCloudError } from "ordercloud-javascript-sdk";

Tokens.Set("INSERT_ACCESS_TOKEN_HERE");
const buyer: Buyer = await Buyers.Create({
  ID: "buyer1",
  Name: "Buyer 1",
  Active: true
})
.catch((err:OrderCloudError) => console.log(err));
console.log(buyer);
csharp
using OrderCloud.SDK;

var client = new OrderCloudClient(...);
Buyer response = await client.Buyers.CreateAsync(new Buyer {
  ID = "buyer1",
  Name = "Buyer 1",
  Active = true
});
Security profile
http
POST https://sandboxapi.ordercloud.io/v1/securityprofiles HTTP/1.1
Authorization: Bearer INSERT_ACCESS_TOKEN_HERE
Content-Type: application/json; charset=UTF-8
{
  "ID": "buyerProfile",
  "Name": "Buyer Security Profile",
  "Roles": [
    "Shopper"
  ]
}
javascript
import { Tokens, SecurityProfiles } from "ordercloud-javascript-sdk";

Tokens.Set("INSERT_ACCESS_TOKEN_HERE");
SecurityProfiles.Create({
  ID: "buyerProfile",
  Name: "Buyer Security Profile",
  Roles: ["Shopper"]
})
.then(response => {
  console.log(response);
})
.catch(err => console.log(err));
typescript
import { Tokens, SecurityProfiles, SecurityProfile, OrderCloudError } from "ordercloud-javascript-sdk";

Tokens.Set("INSERT_ACCESS_TOKEN_HERE");
const securityProfile: SecurityProfile = await SecurityProfiles.Create({
  ID: "buyerProfile",
  Name: "Buyer Security Profile",
  Roles: ["Shopper"]
})
.catch((err:OrderCloudError) => console.log(err));
console.log(securityProfile);
csharp
using OrderCloud.SDK;

var client = new OrderCloudClient(...);
await client.AuthenticateAsync();
SecurityProfile response = await client.SecurityProfiles.CreateAsync(new SecurityProfile {
  ID = "buyerProfile",
  Name = "Buyer Security Profile",
  Roles = new ApiRole[] { ApiRole.Shopper }
});
Security profile assignment
http
POST https://sandboxapi.ordercloud.io/v1/securityprofiles/assignments HTTP/1.1
Authorization: Bearer INSERT_ACCESS_TOKEN_HERE
Content-Type: application/json; charset=UTF-8

{
  "SecurityProfileID": "buyerProfile",
  "BuyerID": "buyer1"
}
javascript
import { Tokens, SecurityProfiles } from "ordercloud-javascript-sdk";

Tokens.Set("INSERT_ACCESS_TOKEN_HERE");
SecurityProfiles.SaveAssignment({
  SecurityProfileID: "buyerProfile",
  BuyerID: "buyer1"
})
.then(() => {
  // no response when security profile assigned
})
.catch(err => console.log(err));
typescript
import { Tokens, SecurityProfiles, OrderCloudError } from "ordercloud-javascript-sdk";

Tokens.Set("INSERT_ACCESS_TOKEN_HERE");
await SecurityProfiles.SaveAssignment({
  SecurityProfileID: "buyerProfile",
  BuyerID: "buyer1"
})
.catch((err:OrderCloudError) => console.log(err));
csharp
using OrderCloud.SDK;

var client = new OrderCloudClient(...);
await client.AuthenticateAsync();
await client.SecurityProfiles.SaveAssignmentAsync(new SecurityProfileAssignment {
  SecurityProfileID = "buyerProfile",
  BuyerID = "buyer1"
});
API client
http
POST https://sandboxapi.ordercloud.io/v1/apiclients HTTP/1.1
Authorization: Bearer INSERT_ACCESS_TOKEN_HERE
Content-Type: application/json; charset=UTF-8

{
  "AccessTokenDuration": 600,
  "Active": true,
  "AppName": "Buyer Client",
  "RefreshTokenDuration": 43200,
  "AllowAnyBuyer": true,
  "AllowSeller": true
}
javascript
import { Tokens, ApiClients } from "ordercloud-javascript-sdk";

Tokens.Set("INSERT_ACCESS_TOKEN_HERE");
ApiClients.Create({
  AccessTokenDuration: 600,
  Active: true,
  AppName: "Buyer Client",
  RefreshTokenDuration: 43200,
  AllowAnyBuyer: true,
  AllowSeller: true
})
.then((response) => {
  console.log(response);
})
.catch(err => console.log(err));
typescript
import { Tokens, ApiClients, ApiClient, OrderCloudError } from "ordercloud-javascript-sdk";

Tokens.Set("INSERT_ACCESS_TOKEN_HERE");
const apiClient: ApiClient = await ApiClients.Create({
  AccessTokenDuration: 600,
  Active: true,
  AppName: "Buyer Client",
  RefreshTokenDuration: 43200,
  AllowAnyBuyer: true,
  AllowSeller: true
})
.catch((err:OrderCloudError) => console.log(err));
console.log(apiClient);
csharp
using OrderCloud.SDK;

var client = new OrderCloudClient(...);
await client.AuthenticateAsync();
ApiClient response = await client.ApiClients.Create(new ApiClient {
  AccessTokenDuration = 600,
  Active = true,
  AppName = "Buyer Client",
  RefreshTokenDuration = 43200,
  AllowAnyBuyer = true,
  AllowSeller = true
});

Record the ID from the response for OpenID Connect configuration.

Configuring OIDC (OpenID Connect) via OrderCloud

Configure Azure

Create an Azure AD B2C tenant

If you haven't yet, you will need to create a new Azure AD B2C tenant. Follow this tutorial for instructions.

Create a user flow

To keep things simple we're setting up azure with user flows but it will work with custom policies as well. Click on "User Flows"

For demo purposes we used the "Sign in" user flow but you can select whichever one makes sense for you. Any claims selected under "Application Claims" will be encoded into the ID token which is accessible during the /createuser and /syncuser endpoint.

Create an App Registration

Under "App Registrations" click on "New Registration"

Make sure you set Redirect URI to OrderCloud's /ocrpcode endpoint. Please note that the base URL for all OrderCloud endpoints vary by environment/region so make sure to check your marketplace for the correct value.

Under "Overview" of your newly created app registration copy the "Application (client) ID", this will be your ConnectClientID in future steps.

While still under "Overview", click on "Endpoints"

There are two values specifically we are interested in:

  • Azure AD B2C OAuth 2.0 token endpoint (v2) - This will be your TokenEndpoint
  • Azure AD B2C OAuth 2.0 authorization endpoint (v2) - This will be your AuthorizationEndpoint

You should replace <policy-name> with the name of the user flow you created previously.

Next, under "Certificates & secrets" create a new client secret. Copy the generated value, this will be your ConnectClientSecret in future steps.

Start ngrok

You'll need a publicly available endpoint. You can use a tool called ngrok to let us do this locally without having to deploy anything. After installing ngrok run the command ngrok http 3000. This tells ngrok to expose our endpoint (not yet running) on http://localhost:3000 to two public endpoints. After running the command copy either one of those URLs and record it, you'll need it for the next step.

We recommend to keep ngrok running. Restarting it will generate unique public endpoints and require you to update your configuration in OrderCloud.

  1. Create OpenID Connect Integration Event:
http
POST https://sandboxapi.ordercloud.io/v1/integrationEvents HTTP/1.1
Authorization: Bearer INSERT_ACCESS_TOKEN_HERE
Content-Type: application/json; charset=UTF-8

{
  "ID": "openidconnect",
  "Name": "openidconnect",
  "EventType": "OpenIDConnect",
  "CustomImplementationUrl": "{your-ngrok-url}/integration-events",
  "HashKey": "supersecrethash",
  "ElevatedRoles": ["BuyerUserAdmin"]
}
javascript
import { Tokens, IntegrationEvents } from "ordercloud-javascript-sdk";

Tokens.Set("INSERT_ACCESS_TOKEN_HERE");
IntegrationEvents.Create({
  ID: "openidconnect",
  Name: "openidconnect",
  EventType: "OpenIDConnect",
  CustomImplementationUrl: "{your-ngrok-url}/integration-events",
  HashKey: "supersecrethash",
  ElevatedRoles: ["BuyerUserAdmin"]
})
.then(response => {
  console.log(response);
})
.catch(err => console.log(err));
typescript
import { Tokens, IntegrationEvents, IntegrationEvent, OrderCloudError } from "ordercloud-javascript-sdk";

Tokens.Set("INSERT_ACCESS_TOKEN_HERE");
const integrationEvent: IntegrationEvent = await IntegrationEvents.Create({
  ID: "openidconnect",
  Name: "openidconnect",
  EventType: "OpenIDConnect",
  CustomImplementationUrl: "{your-ngrok-url}/integration-events",
  HashKey: "supersecrethash",
  ElevatedRoles: ["BuyerUserAdmin"]
})
.catch((err:OrderCloudError) => console.log(err));
console.log(integrationEvent);
csharp
using OrderCloud.SDK;

var client = new OrderCloudClient(...);
await client.AuthenticateAsync();
IntegrationEvent response = await client.IntegrationEvents.CreateAsync(new IntegrationEvent {
  ID = "openidconnect",
  Name = "openidconnect",
  EventType = IntegrationEventType.OpenIDConnect,
  CustomImplementationUrl = "{your-ngrok-url}/integration-events",
  HashKey = "supersecrethash",
  ElevatedRoles = new ApiRole[] { ApiRole.BuyerUserAdmin }
});
OrderCloud propertyDescription
IDUnique identifier for the integration event
NameA short name describing the integration event, this is not user facing
EventTypeIndicates what type of integration event this is, in our case we should use OpenIDConnect
CustomImplementationUrlThis indicates the base URL of your middleware where OrderCloud should post to. For OpenIDConnect it will call out to the pat /createuser and /syncuser
HashKeyThis is an important security feature that is used by your middleware to validate that requests made to your endpoints are legitimate and come from OrderCloud
ElevatedRolesAn optional array of roles that will be encoded in the user's token and sent along in the payload to /createuser and /syncuser. In our case we are defining BuyerUserAdmin so that our middleware endpoints have the roles necessary to create users on the fly.
  1. Create OpenID Connect configuration:
http
POST https://sandboxapi.ordercloud.io/v1/openidconnects HTTP/1.1
Authorization: Bearer INSERT_ACCESS_TOKEN_HERE
Content-Type: application/json; charset=UTF-8

{
  "ID": "azure",
  "OrderCloudApiClientID": "CLIENT_ID_FROM_CREATING_API_CLIENT_STEP",
  "ConnectClientID": "CLIENT_ID_FROM_CONFIGURING_AZURE",
  "ConnectClientSecret": "CLIENT_SECRET_FROM_CONFIGURING_AZURE",
  "AppStartUrl": "http://localhost:3000?token={0}&idpToken={1}&refreshToken={3}",
  "AuthorizationEndpoint": "AUTH_ENDPOINT_FROM_AZURE",
  "TokenEndpoint": "TOKEN_ENDPOINT_FROM_AZURE",
  "UrlEncoded": false,
  "CallSyncUserIntegrationEvent": true,
  "IntegrationEventID": "openidconnect",
  "AdditionalIdpScopes": ["CLIENT_ID_FROM_CONFIGURING_AZURE"]
}
javascript
import { Tokens, OpenIdConnects } from "ordercloud-javascript-sdk";

Tokens.Set("INSERT_ACCESS_TOKEN_HERE");
OpenIdConnects.Create({
  ID: "azure",
  OrderCloudApiClientID: "CLIENT_ID_FROM_CREATING_API_CLIENT_STEP",
  ConnectClientID: "CLIENT_ID_FROM_CONFIGURING_AZURE",
  ConnectClientSecret: "CLIENT_SECRET_FROM_CONFIGURING_AZURE",
  AppStartUrl: "http://localhost:3000?token={0}&idpToken={1}&refreshToken={3}",
  AuthorizationEndpoint: "AUTH_ENDPOINT_FROM_AZURE",
  TokenEndpoint: "TOKEN_ENDPOINT_FROM_AZURE",
  UrlEncoded: false,
  CallSyncUserIntegrationEvent: true,
  IntegrationEventID: "openidconnect",
  AdditionalIdpScopes: ["CLIENT_ID_FROM_CONFIGURING_AZURE"]
})
.then(response => {
  console.log(response);
})
.catch(err => console.log(err));
typescript
import { Tokens, OpenIdConnects, OpenIdConnect, OrderCloudError } from "ordercloud-javascript-sdk";

Tokens.Set("INSERT_ACCESS_TOKEN_HERE");
const openIdConnect = await OpenIdConnects.Create({
  ID: "azure",
  OrderCloudApiClientID: "CLIENT_ID_FROM_CREATING_API_CLIENT_STEP",
  ConnectClientID: "CLIENT_ID_FROM_CONFIGURING_AZURE",
  ConnectClientSecret: "CLIENT_SECRET_FROM_CONFIGURING_AZURE",
  AppStartUrl: "http://localhost:3000?token={0}&idpToken={1}&refreshToken={3}",
  AuthorizationEndpoint: "AUTH_ENDPOINT_FROM_AZURE",
  TokenEndpoint: "TOKEN_ENDPOINT_FROM_AZURE",
  UrlEncoded: false,
  CallSyncUserIntegrationEvent: true,
  IntegrationEventID: "openidconnect",
  AdditionalIdpScopes: ["CLIENT_ID_FROM_CONFIGURING_AZURE"]
})
.catch((err:OrderCloudError) => console.log(err));
console.log(openIdConnect);
csharp
using OrderCloud.SDK;

var client = new OrderCloudClient(...);
await client.AuthenticateAsync();
OpenIdConnect response = await client.OpenIdConnects.CreateAsync(new OpenIdConnect {
  ID = "azure",
  OrderCloudApiClientID = "CLIENT_ID_FROM_CREATING_API_CLIENT_STEP",
  ConnectClientID = "CLIENT_ID_FROM_CONFIGURING_AZURE",
  ConnectClientSecret = "CLIENT_SECRET_FROM_CONFIGURING_AZURE",
  AppStartUrl = "http://localhost:3000?token={0}&idpToken={1}&refreshToken={3}",
  AuthorizationEndpoint = "AUTH_ENDPOINT_FROM_AZURE",
  TokenEndpoint = "TOKEN_ENDPOINT_FROM_AZURE",
  UrlEncoded = false,
  CallSyncUserIntegrationEvent = true,
  IntegrationEventID = "openidconnect",
  AdditionalIdpScopes = ["CLIENT_ID_FROM_CONFIGURING_AZURE"]
});

Testing

  1. Clone openidconnect-nextjs
  2. Install dependencies: npm install
  3. Configure environment: Copy .env.example to .env.local
  4. Start server: npm run start
  5. Test: Navigate to http://localhost:3000

Review /createuser and /syncuser endpoints for implementation details.

Troubleshooting

Common issue: "error validating token with authority"

  • Verify configuration values:
    • ConnectClientID
    • ConnectClientSecret
    • OrderCloudClientID

Implementing SSO with Google IDP

OpenID Connect is a powerful feature that enables you to provide single sign-on capabilities for any identity provider that supports the specification. In this tutorial we'll walk you step by step through what you'll need to get single sign-on working by using Google as the identity provider. By the end of this tutorial, you'll be able to sign in via Google and be logged into OrderCloud.

Demo

Before we start, let's take a look at the finished product. Navigate to this website. You will be redirected to Google's sign-in page and after successfully signing in you should see your login details including:

  • Currently authenticated user
  • OrderCloud Access Token
  • OrderCloud Refresh Token (if configured)
  • Google ID Token

Marketplace configuration

First, you need to access your Marketplace in the Sitecore Cloud Portal.

After accessing, take special note of the OrderCloud Base API URL, which identifies the base URL needed for all API requests.

For this demonstration we are on the Sandbox environment in the region Us-West so our base API URL is https://sandboxapi.ordercloud.io, yours may look different.

Create supporting entities

We will be creating a single-sign-on experience for buyer users specifically, so we'll create the most basic OrderCloud entities required to support that scenario.

Create a buyer organization

http
POST sandboxapi.ordercloud.io/v1/buyers HTTP/1.1
Authorization: Bearer INSERT_ACCESS_TOKEN_HERE
Content-Type: application/json; charset=UTF-8
json
{
  "ID": "buyer1",
  "Name": "Buyer 1",
  "Active": true
}
javascript
import { Tokens, Buyers } from "ordercloud-javascript-sdk";

Tokens.Set("INSERT_ACCESS_TOKEN_HERE");
Buyers.Create({
  ID: "buyer1",
  Name: "Buyer 1",
  Active: true
})
.then(response => {
  // returns the newly created buyer organization
  console.log(response);
})
.catch(err => console.log(err));
typescript
import { Tokens, Buyers, Buyer, OrderCloudError } from "ordercloud-javascript-sdk";

Tokens.Set("INSERT_ACCESS_TOKEN_HERE");
const buyer: Buyer = await Buyers.Create({
  ID: "buyer1",
  Name: "Buyer 1",
  Active: true
})
.catch((err:OrderCloudError) => console.log(err));
console.log(buyer);
csharp
using OrderCloud.SDK;

var client = new OrderCloudClient(...);
Buyer response = await client.Buyers.CreateAsync(new Buyer {
  ID = "buyer1",
  Name = "Buyer 1",
  Active = true
});

Create security profile

http
POST https://sandboxapi.ordercloud.io/v1/securityprofiles HTTP/1.1
Authorization: Bearer INSERT_ACCESS_TOKEN_HERE
Content-Type: application/json; charset=UTF-8
json
{
  "ID": "buyerProfile",
  "Name": "Buyer Security Profile",
  "Roles": [
    "Shopper"
  ]
}
javascript
import { Tokens, SecurityProfiles } from "ordercloud-javascript-sdk";

Tokens.Set("INSERT_ACCESS_TOKEN_HERE");
SecurityProfiles.Create({
  ID: "buyerProfile",
  Name: "Buyer Security Profile",
  Roles: ["Shopper"]
})
.then(response => {
  // returns the newly created security profile
  console.log(response);
})
.catch(err => console.log(err));
typescript
import { Tokens, SecurityProfiles, SecurityProfile, OrderCloudError } from "ordercloud-javascript-sdk";

Tokens.Set("INSERT_ACCESS_TOKEN_HERE");
const securityProfile: SecurityProfile = await SecurityProfiles.Create({
  ID: "buyerProfile",
  Name: "Buyer Security Profile",
  Roles: ["Shopper"]
})
.catch((err:OrderCloudError) => console.log(err));
console.log(securityProfile);
csharp
using OrderCloud.SDK;

var client = new OrderCloudClient(...);
await client.AuthenticateAsync();
SecurityProfile response = await client.SecurityProfiles.CreateAsync(new SecurityProfile {
  ID = "buyerProfile",
  Name = "Buyer Security Profile",
  Roles = new ApiRole[] { ApiRole.Shopper }
});

Assign security profile

http
POST https://sandboxapi.ordercloud.io/v1/securityprofiles/assignments HTTP/1.1
Authorization: Bearer INSERT_ACCESS_TOKEN_HERE
Content-Type: application/json; charset=UTF-8
json
{
  "SecurityProfileID": "buyerProfile",
  "BuyerID": "buyer1"
}
javascript
import { Tokens, SecurityProfiles } from "ordercloud-javascript-sdk";

Tokens.Set("INSERT_ACCESS_TOKEN_HERE");
SecurityProfiles.SaveAssignment({
  SecurityProfileID: "buyerProfile",
  BuyerID: "buyer1"
})
.then(() => {
  // no response when security profile assigned
})
.catch(err => console.log(err));
typescript
import { Tokens, SecurityProfiles, OrderCloudError } from "ordercloud-javascript-sdk";

Tokens.Set("INSERT_ACCESS_TOKEN_HERE");
await SecurityProfiles.SaveAssignment({
  SecurityProfileID: "buyerProfile",
  BuyerID: "buyer1"
})
.catch((err:OrderCloudError) => console.log(err));
csharp
using OrderCloud.SDK;

var client = new OrderCloudClient(...);
await client.AuthenticateAsync();
await client.SecurityProfiles.SaveAssignmentAsync(new SecurityProfileAssignment {
  SecurityProfileID = "buyerProfile",
  BuyerID = "buyer1"
});

Create API client

http
POST https://sandboxapi.ordercloud.io/v1/apiclients HTTP/1.1
Authorization: Bearer INSERT_ACCESS_TOKEN_HERE
Content-Type: application/json; charset=UTF-8
json
{
  "AccessTokenDuration": 600,
  "Active": true,
  "AppName": "Buyer Client",
  "RefreshTokenDuration": 43200,
  "AllowAnyBuyer": true,
  "AllowSeller": true
}
javascript
import { Tokens, ApiClients } from "ordercloud-javascript-sdk";

Tokens.Set("INSERT_ACCESS_TOKEN_HERE");
ApiClients.Create({
  AccessTokenDuration: 600,
  Active: true,
  AppName: "Buyer Client",
  RefreshTokenDuration: 43200,
  AllowAnyBuyer: true,
  AllowSeller: true
})
.then((response) => {
  console.log(response);
})
.catch(err => console.log(err));
typescript
import { Tokens, ApiClients, ApiClient, OrderCloudError } from "ordercloud-javascript-sdk";

Tokens.Set("INSERT_ACCESS_TOKEN_HERE");
const apiClient: ApiClient = await ApiClients.Create({
  AccessTokenDuration: 600,
  Active: true,
  AppName: "Buyer Client",
  RefreshTokenDuration: 43200,
  AllowAnyBuyer: true,
  AllowSeller: true
})
.catch((err:OrderCloudError) => console.log(err));
console.log(apiClient);
csharp
using OrderCloud.SDK;

var client = new OrderCloudClient(...);
await client.AuthenticateAsync();
ApiClient response = await client.ApiClients.Create(new ApiClient {
  AccessTokenDuration = 600,
  Active = true,
  AppName = "Buyer Client",
  RefreshTokenDuration = 43200,
  AllowAnyBuyer = true,
  AllowSeller = true
});

Record the ID from the response for the OpenID Connect configuration.

OpenID Connect configuration

Start ngrok

We'll need a publicly available endpoint. We can use a tool called ngrok to let us do this locally without having to deploy anything. After installing ngrok run the command ngrok http 3000. This tells ngrok to expose our endpoint (not yet running) on http://localhost:3000 to two public endpoints. After running the command copy either one of those URLs and record it, we'll need when creating a new OpenID Connect.

We recommend to keep ngrok running. Restarting it will generate unique public endpoints and require you to update your configuration in OrderCloud.

Create integration event

http
POST https://sandboxapi.ordercloud.io/v1/integrationEvents HTTP/1.1
Authorization: Bearer INSERT_ACCESS_TOKEN_HERE
Content-Type: application/json; charset=UTF-8
json
{
  "ID": "openidconnect",
  "Name": "openidconnect",
  "EventType": "OpenIDConnect",
  "CustomImplementationUrl": "{your-ngrok-url}/integration-events",
  "HashKey": "supersecrethash",
  "ElevatedRoles": [
    "BuyerUserAdmin"
  ]
}
javascript
import { Tokens, IntegrationEvents } from "ordercloud-javascript-sdk";

Tokens.Set("INSERT_ACCESS_TOKEN_HERE");
IntegrationEvents.Create({
  ID: "openidconnect",
  Name: "openidconnect",
  EventType: "OpenIDConnect",
  CustomImplementationUrl: "{your-ngrok-url}/integration-events",
  HashKey: "supersecrethash",
  ElevatedRoles: ["BuyerUserAdmin"]
})
.then(response => {
  // returns the newly created integration event
  console.log(response);
})
.catch(err => console.log(err));
typescript
import { Tokens, IntegrationEvents, IntegrationEvent, OrderCloudError } from "ordercloud-javascript-sdk";

Tokens.Set("INSERT_ACCESS_TOKEN_HERE");
const integrationEvent: IntegrationEvent = await IntegrationEvents.Create({
  ID: "openidconnect",
  Name: "openidconnect",
  EventType: "OpenIDConnect",
  CustomImplementationUrl: "{your-ngrok-url}/integration-events",
  HashKey: "supersecrethash",
  ElevatedRoles: ["BuyerUserAdmin"]
})
.catch((err:OrderCloudError) => console.log(err));
console.log(integrationEvent);
csharp
using OrderCloud.SDK;

var client = new OrderCloudClient(...);
await client.AuthenticateAsync();
IntegrationEvent response = await client.IntegrationEvents.CreateAsync(new IntegrationEvent {
  ID = "openidconnect",
  Name = "openidconnect",
  EventType = IntegrationEventType.OpenIDConnect,
  CustomImplementationUrl = "{your-ngrok-url}/integration-events",
  HashKey = "supersecrethash",
  ElevatedRoles = new ApiRole[] { ApiRole.BuyerUserAdmin }
});

Configure Google

Follow Google's instructions for setting up OpenID Connect configuration on their side. You'll need to set the authorized redirect URI to {ordercloud_base_url}/ocrpcode. Take note of the clientID and clientSecret which OrderCloud will refer to as ConnectClientID and ConnectClientSecret respectively, these values will be needed in the following step.

Create a new OpenID Connect

This entity configures the connection between Google and OrderCloud.

http
POST https://sandboxapi.ordercloud.io/v1/openidconnects HTTP/1.1
Authorization: Bearer INSERT_ACCESS_TOKEN_HERE
Content-Type: application/json; charset=UTF-8
json
{
  "ID": "google",
  "OrderCloudApiClientID": "CLIENT_ID_FROM_CREATING_API_CLIENT_STEP",
  "ConnectClientID": "GOOGLE_CLIENT_ID_HERE",
  "ConnectClientSecret": "GOOGLE_CLIENT_SECRET_HERE",
  "AppStartUrl": "http://localhost:3000?token={0}&idpToken={1}",
  "AuthorizationEndpoint": "https://accounts.google.com/o/oauth2/v2/auth",
  "TokenEndpoint": "https://oauth2.googleapis.com/token",
  "UrlEncoded": false,
  "CallSyncUserIntegrationEvent": true,
  "IntegrationEventID": "openidconnect",
  "AdditionalIdpScopes": []
}
javascript
import { Tokens, OpenIdConnects } from "ordercloud-javascript-sdk";

Tokens.Set("INSERT_ACCESS_TOKEN_HERE");
OpenIdConnects.Create({
  ID: "google",
  OrderCloudApiClientID: "CLIENT_ID_FROM_CREATING_API_CLIENT_STEP",
  ConnectClientID: "GOOGLE_CLIENT_ID_HERE",
  ConnectClientSecret: "GOOGLE_CLIENT_SECRET_HERE",
  AppStartUrl: "http://localhost:3000?token={0}&idpToken={1}",
  AuthorizationEndpoint: "https://accounts.google.com/o/oauth2/v2/auth",
  TokenEndpoint: "https://oauth2.googleapis.com/token",
  UrlEncoded: false,
  CallSyncUserIntegrationEvent: true,
  IntegrationEventID: "openidconnect",
  AdditionalIdpScopes: []
})
.then(response => {
  // returns the newly created openidconnect
  console.log(response);
})
.catch(err => console.log(err));
typescript
import { Tokens, OpenIdConnects, OpenIdConnect, OrderCloudError } from "ordercloud-javascript-sdk";

Tokens.Set("INSERT_ACCESS_TOKEN_HERE");
const openIdConnect = await OpenIdConnects.Create({
  ID: "google",
  OrderCloudApiClientID: "CLIENT_ID_FROM_CREATING_API_CLIENT_STEP",
  ConnectClientID: "GOOGLE_CLIENT_ID_HERE",
  ConnectClientSecret: "GOOGLE_CLIENT_SECRET_HERE",
  AppStartUrl: "http://localhost:3000?token={0}&idpToken={1}",
  AuthorizationEndpoint: "https://accounts.google.com/o/oauth2/v2/auth",
  TokenEndpoint: "https://oauth2.googleapis.com/token",
  UrlEncoded: false,
  CallSyncUserIntegrationEvent: true,
  IntegrationEventID: "openidconnect",
  AdditionalIdpScopes: []
})
.catch((err:OrderCloudError) => console.log(err));
console.log(openIdConnect);
csharp
using OrderCloud.SDK;

var client = new OrderCloudClient(...);
await client.AuthenticateAsync();
OpenIdConnect response = await client.OpenIdConnects.CreateAsync(new OpenIdConnect {
  ID = "google",
  OrderCloudApiClientID = "CLIENT_ID_FROM_CREATING_API_CLIENT_STEP",
  ConnectClientID = "GOOGLE_CLIENT_ID_HERE",
  ConnectClientSecret = "GOOGLE_CLIENT_SECRET_HERE",
  AppStartUrl = "http://localhost:3000?token={0}&idpToken={1}",
  AuthorizationEndpoint = "https://accounts.google.com/o/oauth2/v2/auth",
  TokenEndpoint = "https://oauth2.googleapis.com/token",
  UrlEncoded = false,
  CallSyncUserIntegrationEvent = true,
  IntegrationEventID = "openidconnect",
  AdditionalIdpScopes = []
});

Testing

OrderCloud and Google should now be completely configured, and you are ready to test to make sure everything is working. To simplify this aspect, we've created a very minimal frontend to test this functionality.

  1. Clone this repository
  2. Install dependencies by running npm install at the root of the project
  3. Copy .env.example to .env.local
  4. Run the project by running npm run start. This will start the server on port 3000. Remember ngrok is already listening to this port and will expose our endpoints publicly.
  5. Navigate to the url http://localhost:3000. If everything is correct you should be redirected to Google's login page. Upon signing in you will be redirected back to the application and should see details about your logged in user

Be sure to look at the /createuser and /syncuser endpoints

Common Issues

Error message: "error validating token with authority"

This issue occurs when OrderCloud attempts to retrieve the ID token from the IDP. This is generally a configuration issue. Confirm ConnectClientID, ConnectClientSecret, and OrderCloudClientID are correct.


If you have suggestions for improving this article, let us know!