Configuring User and Application Data Access
Published by DJ Steinmetz on October 20, 2022
Prerequisites
In order to configure access to our marketplace data, we will need access to a marketplace. Create a Sandbox marketplace, or follow this tutorial to configure data access for an existing marketplace.
Assumptions
For this tutorial, we are using the URL for the sandbox environment in the region US-West.
You have an active buyer user with ID and Username
buyeruser
, with a Password "Supersecurepassword123!"You have an active seller user with ID and Username
selleruser
, with a Password "Supersecurepassword123!"You have an active buyer company with ID
buyercompany
Any ClientID value that is used in example requests should be replaced with the real ClientIDs that are created from making the requests in this tutorial. For our example, let's assume our ClientIDs are:
Buyer storefront ClientID:
"00000000-0000-0000-0000-000000000000"
Admin backoffice ClientID:
"11111111-1111-1111-1111-111111111111"
Middlware API ClientID:
"222222222-2222-2222-2222-222222222222"
Middleware API Client Secret:
"supersecureclientsecretstring"
You have created a new marketplace, or have access to an existing marketplace in the OrderCloud Portal
You have and maintain a buyer storefront application, an admin backoffice application and a custom middleware API solution that handles more sensitive data and any custom integrations to OrderCloud
Creating API Clients
Before we can configure data access for our applications, we need to create API Clients. API Clients are like more complex API keys that grant our different applications certain configured access to our marketplace data. From a security perspective, it is best practice to have one API Client per entry-point to your marketplace data.
Create an API Client to represent a buyer storefront application
First, let's create an API Client for our buyer storefront application. This API Client will be the gateway for our buyer application to gain access to our marketplace data. We'll name it Buyer Storefront
, designate it active, and make the access token duration 600 (minutes). Additionally, we will specify this API Client with AllowAnyBuyer: true
which means that any buyer user will be able to successfully authenticate against this API Client. If a seller or supplier user attempts to authenticate against this API Client, they will get a 401 Unauthorized
response.
1POST https://sandboxapi.ordercloud.io/v1/apiclients HTTP/1.12Authorization: Bearer INSERT_ACCESS_TOKEN_HERE3Content-Type: application/json; charset=UTF-845{6 "AppName": "Buyer Storefront",7 "Active": true,8 "AccessTokenDuration": 600,9 "AllowAnyBuyer": true10}
1import { ApiClients } from "ordercloud-javascript-sdk";23ApiClients.Create({4 "AppName": "Buyer Storefront",5 "Active": true,6 "AccessTokenDuration": 600,7 "AllowAnyBuyer": true8})9.then((apiClient) => {10 console.log(apiClient);11})12.catch((ex) => console.log(ex));
1import { ApiClients, ApiClient, OrderCloudError } from "ordercloud-javascript-sdk";23ApiClients.Create({4 "AppName": "Buyer Storefront",5 "Active": true,6 "AccessTokenDuration": 600,7 "AllowAnyBuyer": true8})9.then((apiClient: ApiClient) => {10 console.log(apiClient);11})12.catch((ex: OrderCloudError) => console.log(ex));
1using OrderCloud.SDK;23var data = new ApiClient4{5 "AppName": "Buyer Storefront",6 "Active": true,7 "AccessTokenDuration": 600,8 "AllowAnyBuyer": true9};10try {11 ApiClient response = await ApiClients.Create(data);12 Console.WriteLine(response)13} catch(OrderCloudException ex) {14 Console.WriteLine(ex.Message);15}
Create an API Client to represent an admin backoffice application
Next, let's create an API Client for our admin backoffice application. This API Client will be the gateway for our backoffice application to gain access to our marketplace data. We'll name it Admin Backoffice
, designate it active, and make the access token duration 600 (minutes). Additionally, we will specify this API Client with AllowAnySeller: true
and AllowAnySupplier: true
. In our admin interface, we may have suppliers that need to login to administer their products. Sellers may need to login to administer buyers, catalogs or seller-owned products. If a buyer user attempts to authenticate against this API Client, they will get a 401 Unauthorized
response.
1POST https://sandboxapi.ordercloud.io/v1/apiclients HTTP/1.12Authorization: Bearer INSERT_ACCESS_TOKEN_HERE3Content-Type: application/json; charset=UTF-845{6 "AppName": "Admin Backoffice",7 "Active": true,8 "AccessTokenDuration": 600,9 "AllowAnySeller": true,10 "AllowAnySupplier": true11}
1import { ApiClients } from "ordercloud-javascript-sdk";23ApiClients.Create({4 "AppName": "Admin Backoffice",5 "Active": true,6 "AccessTokenDuration": 600,7 "AllowAnySeller": true,8 "AllowAnySupplier": true9})10.then((apiClient) => {11 console.log(apiClient);12})13.catch((ex) => console.log(ex));
1import { ApiClients, ApiClient, OrderCloudError } from "ordercloud-javascript-sdk";23ApiClients.Create({4 "AppName": "Admin Backoffice",5 "Active": true,6 "AccessTokenDuration": 600,7 "AllowAnySeller": true,8 "AllowAnySupplier": true9})10.then((apiClient: ApiClient) => {11 console.log(apiClient);12})13.catch((ex: OrderCloudError) => console.log(ex));
1using OrderCloud.SDK;23var data = new ApiClient4{5 "AppName": "Admin Backoffice",6 "Active": true,7 "AccessTokenDuration": 600,8 "AllowAnySeller": true,9 "AllowAnySupplier": true10};11try {12 ApiClient response = await ApiClients.Create(data);13 Console.WriteLine(response)14} catch(OrderCloudException ex) {15 Console.WriteLine(ex.Message);16}
Create an API Client to represent a middleware API solution
Now, It is a fairly common use case for an application tech stack to require a custom middleware solution to handle custom integrations to OrderCloud, or other elevated functions that we want to protect behind an API. To configure access to our marketplace data for our custom middleware solution, we will create an API Client and set a Client Secret. We will need a Client Secret for this API Client because our middleware API will authenticate to OrderCloud using the Client Credentials grant type, being that a system is authenticating, not a "user" with a Username and Password. We'll name it Middleware API
, designate it active, and make the access token duration 600 (minutes). Additionally, we will specify this API Client with AllowAnySeller: true
. Since we have specified a Client Secret on this API Client, the Client Secret will need to be passed with any authentication request, regardless of grant type. If any user attempts to authenticate with the Password grant type, and does not pass the Client Secret, they will get a 401 Unauthorized
response. Remember: Client Secret is a sensitive data point, and should never be publicly exposed. Configure your applications with environment variables to handle this value. If the value is ever exposed, immediately revoke the API Client or re-generate the Client Secret.
1POST https://sandboxapi.ordercloud.io/v1/apiclients HTTP/1.12Authorization: Bearer INSERT_ACCESS_TOKEN_HERE3Content-Type: application/json; charset=UTF-845{6 "AppName": "Middleware API",7 "Active": true,8 "AccessTokenDuration": 600,9 "AllowAnySeller": true,10 "ClientSecret": "supersecureclientsecretstring"11}
1import { ApiClients } from "ordercloud-javascript-sdk";23ApiClients.Create({4 "AppName": "Admin Backoffice",5 "Active": true,6 "AccessTokenDuration": 600,7 "AllowAnySeller": true,8 "ClientSecret": "supersecureclientsecretstring"9})10.then((apiClient) => {11 console.log(apiClient);12})13.catch((ex) => console.log(ex));
1import { ApiClients, ApiClient, OrderCloudError } from "ordercloud-javascript-sdk";23ApiClients.Create({4 "AppName": "Admin Backoffice",5 "Active": true,6 "AccessTokenDuration": 600,7 "AllowAnySeller": true,8 "ClientSecret": "supersecureclientsecretstring"9})10.then((apiClient: ApiClient) => {11 console.log(apiClient);12})13.catch((ex: OrderCloudError) => console.log(ex));
1using OrderCloud.SDK;23var data = new ApiClient4{5 "AppName": "Admin Backoffice",6 "Active": true,7 "AccessTokenDuration": 600,8 "AllowAnySeller": true,9 "ClientSecret": "supersecureclientsecretstring"10};11try {12 ApiClient response = await ApiClients.Create(data);13 Console.WriteLine(response)14} catch(OrderCloudException ex) {15 Console.WriteLine(ex.Message);16}
Creating Security Profiles
Now that we have our applications configured with specific access to our marketplace data using API Clients, we should create several Security Profiles to configure data access for our individual users.
Create a buyer security profile
Our buyer security profile will represent the breadth of data access we wish to grant to our buyer users, configured using granular API roles. Let's create a security profile, name it "Default Buyer" with a self-descriptive ID, and specify the Shopper
role in the Roles array, which enables all actions of the shopping experience.
1POST https://sandboxapi.ordercloud.io/v1/securityprofiles HTTP/1.12Authorization: Bearer INSERT_ACCESS_TOKEN_HERE3Content-Type: application/json; charset=UTF-845{6 "ID": "defaultbuyer",7 "Name": "Default Buyer",8 "Roles": [9 "Shopper"10 ]11}
1import { SecurityProfiles } from "ordercloud-javascript-sdk";23SecurityProfiles.Create({4 "ID": "defaultbuyer",5 "Name": "Default Buyer",6 "Roles": [7 "Shopper"8 ]9})10.then((securityProfile) => {11 console.log(securityProfile);12})13.catch((ex) => console.log(ex));
1import { SecurityProfiles, SecurityProfile, OrderCloudError } from "ordercloud-javascript-sdk";23SecurityProfiles.Create({4 "ID": "defaultbuyer",5 "Name": "Default Buyer",6 "Roles": [7 "Shopper"8 ]9})10.then((securityProfile: SecurityProfile) => {11 console.log(securityProfile);12})13.catch((ex: OrderCloudError) => console.log(ex));
1using OrderCloud.SDK;23var data = new SecurityProfile4{5 "ID": "defaultbuyer",6 "Name": "Default Buyer",7 "Roles": new ApiRole[] { ApiRole.Shopper }8};9try {10 SecurityProfile response = await SecurityProfiles.Create(data);11 Console.WriteLine(response)12} catch(OrderCloudException ex) {13 Console.WriteLine(ex.Message);14}
Create a seller security profile
Next, we can create a security profile for our seller users. Let's create a security profile, name it "Default Seller" with a self-descriptive ID, and specify the ProductAdmin
, BuyerAdmin
, and OrderAdmin
roles in the Roles array, which gives our sellers access to administer those specific resources in the API.
As a best practice, you want to control the roles on a granular level and only assign roles necessary for the user as a security precaution. For example, the Admins in our application only need to administer Buyers, Products and Orders. You can find more details on security profiles here.
1POST https://sandboxapi.ordercloud.io/v1/securityprofiles HTTP/1.12Authorization: Bearer INSERT_ACCESS_TOKEN_HERE3Content-Type: application/json; charset=UTF-845{6 "ID": "defaultseller",7 "Name": "Default Seller",8 "Roles": [9 "BuyerAdmin",10 "ProductAdmin",11 "OrderAdmin"12 ]13}
1import { SecurityProfiles } from "ordercloud-javascript-sdk";23SecurityProfiles.Create({4 "ID": "defaultseller",5 "Name": "Default Seller",6 "Roles": [7 "BuyerAdmin",8 "ProductAdmin",9 "OrderAdmin"10 ]11})12.then((securityProfile) => {13 console.log(securityProfile);14})15.catch((ex) => console.log(ex));
1import { SecurityProfiles, SecurityProfile, OrderCloudError } from "ordercloud-javascript-sdk";23SecurityProfiles.Create({4 "ID": "defaultseller",5 "Name": "Default Seller",6 "Roles": [7 "BuyerAdmin",8 "ProductAdmin",9 "OrderAdmin"10 ]11})12.then((securityProfile: SecurityProfile) => {13 console.log(securityProfile);14})15.catch((ex: OrderCloudError) => console.log(ex));
1using OrderCloud.SDK;23var data = new SecurityProfile4{5 "ID": "defaultseller",6 "Name": "Default Seller",7 "Roles": new ApiRole[] { ApiRole.BuyerAdmin, ApiRole.ProductAdmin, ApiRole.OrderAdmin }8};9try {10 SecurityProfile response = await SecurityProfiles.Create(data);11 Console.WriteLine(response)12} catch(OrderCloudException ex) {13 Console.WriteLine(ex.Message);14}
Assign security profiles to the our marketplace entities
Now that we have our Security Profiles created, we need to assign them to the right entities. This is the final step before we can make requests to authenticate our users against the API Clients we created earlier. Let's make a request to assign our defaultbuyer
security profile to our buyer company with ID buyercompany
. Once we have successfully made this assignment, these roles will cascade down through any user under this buyer company. If successful, you should receive a 204 Created
response.
1POST https://sandboxapi.ordercloud.io/v1/securityprofiles/assignments HTTP/1.12Authorization: Bearer INSERT_ACCESS_TOKEN_HERE3Content-Type: application/json; charset=UTF-845{6 "SecurityProfileID": "defaultbuyer",7 "BuyerID": "buyercompany"8}
1import { SecurityProfiles } from "ordercloud-javascript-sdk";23SecurityProfiles.SaveAssignment({4 "SecurityProfileID": "defaultbuyer",5 "BuyerID": "buyercompany"6})7.then(() => {8 console.log("Assignment successful!");9})10.catch((ex) => console.log(ex));
1import { SecurityProfiles, SecurityProfile, OrderCloudError } from "ordercloud-javascript-sdk";23SecurityProfiles.SaveAssignment({4 "SecurityProfileID": "defaultbuyer",5 "BuyerID": "buyercompany"6})7.then(() => {8 console.log("Assignment successful!");9})10.catch((ex: OrderCloudError) => console.log(ex));
1using OrderCloud.SDK;23var data = new SecurityProfileAssignment4{5 "SecurityProfileID": "defaultbuyer",6 "BuyerID": "buyercompany"7};8try {9 await SecurityProfiles.SaveAssignment(data);10 Console.WriteLine("Assignment successful!")11} catch(OrderCloudException ex) {12 Console.WriteLine(ex.Message);13}
Next, let's make a request to assign our defaultseller
security profile to our seller. To assign a security profile to all seller users, the only thing we specify in the request body is the ID of the security profile. If successful, you should receive a 204 Created
response.
1POST https://sandboxapi.ordercloud.io/v1/securityprofiles/assignments HTTP/1.12Authorization: Bearer INSERT_ACCESS_TOKEN_HERE3Content-Type: application/json; charset=UTF-845{6 "SecurityProfileID": "defaultseller"7}
1import { SecurityProfiles } from "ordercloud-javascript-sdk";23SecurityProfiles.SaveAssignment({4 "SecurityProfileID": "defaultseller"5})6.then(() => {7 console.log("Assignment successful!");8})9.catch((ex) => console.log(ex));
1import { SecurityProfiles, SecurityProfile, OrderCloudError } from "ordercloud-javascript-sdk";23SecurityProfiles.SaveAssignment({4 "SecurityProfileID": "defaultseller"5})6.then(() => {7 console.log("Assignment successful!");8})9.catch((ex: OrderCloudError) => console.log(ex));
1using OrderCloud.SDK;23var data = new SecurityProfileAssignment4{5 "SecurityProfileID": "defaultseller"6};7try {8 await SecurityProfiles.SaveAssignment(data);9 Console.WriteLine("Assignment successful!")10} catch(OrderCloudException ex) {11 Console.WriteLine(ex.Message);12}
Make authentication requests
Now that we have configured application data access with API Clients, user access with Security Profiles and made all necessary assignments, let's make some authentication requests to check our handy work. First, let's call to authenticate our buyer user against our buyer API client we created earlier.
1POST https://sandboxapi.ordercloud.io/v1/outh/token HTTP/1.12Content-Type: application/x-www-form-urlencoded;34client_id=00000000-0000-0000-0000-000000000000&grant_type=password&username=buyeruser&password=Supersecurepassword123!&scope=Shopper
1import { Auth } from "ordercloud-javascript-sdk";23Auth.Login("defaultbuyer", "Supersecurepassword123!", "00000000-0000-0000-0000-000000000000", ["Shopper"])4.then((authResponse) => {5 console.log(authResponse.access_token);6})7.catch((ex) => console.log(ex));
1import { Auth, AccessToken, OrderCloudError } from "ordercloud-javascript-sdk";23Auth.Login("defaultbuyer", "Supersecurepassword123!", "00000000-0000-0000-0000-000000000000", ["Shopper"])4.then((authResponse: AccessToken) => {5 console.log(authResponse.access_token);6})7.catch((ex: OrderCloudError) => console.log(ex));
1using OrderCloud.SDK;23try {4 AccessToken response = await ocClient.AuthenticateAsync("00000000-0000-0000-0000-000000000000", "defaultbuyer", "Supersecurepassword123!", new ApiRole[] { ApiRole.Shopper });5 Console.WriteLine(response)6} catch(OrderCloudException ex) {7 Console.WriteLine(ex.Message);8}
With a successful response, we should receive an access token back, with which we can take to JWT.io to debug and see our roles reflected, awesome!
Next, let's make a similar request, but to authenticate our seller user against our seller API Client this time.
1POST https://sandboxapi.ordercloud.io/v1/outh/token HTTP/1.12Content-Type: application/x-www-form-urlencoded;34client_id=11111111-1111-1111-1111-111111111111&grant_type=password&username=selleruser&password=Supersecurepassword123!&scope=FullAccess
1import { Auth } from "ordercloud-javascript-sdk";23Auth.Login("selleruser", "Supersecurepassword123!", "11111111-1111-1111-1111-111111111111", ["FullAccess"])4.then((authResponse) => {5 console.log(authResponse.access_token);6})7.catch((ex) => console.log(ex));
1import { Auth, AccessToken, OrderCloudError } from "ordercloud-javascript-sdk";23Auth.Login("selleruser", "Supersecurepassword123!", "11111111-1111-1111-1111-111111111111", ["FullAccess"])4.then((authResponse: AccessToken) => {5 console.log(authResponse.access_token);6})7.catch((ex: OrderCloudError) => console.log(ex));
1using OrderCloud.SDK;23try {4 AccessToken response = await ocClient.AuthenticateAsync("11111111-1111-1111-1111-111111111111", "selleruser", "Supersecurepassword123!", new ApiRole[] { ApiRole.FullAccess });5 Console.WriteLine(response)6} catch(OrderCloudException ex) {7 Console.WriteLine(ex.Message);8}
Lastly, if we are authenticating a "system" rather than an actual user with a Username and Password, we would use our middleware API Client we created, with the client_credentials
grant type. Let's authenticate against our middleware API Client we created earlier.
1POST https://sandboxapi.ordercloud.io/v1/outh/token HTTP/1.12Content-Type: application/x-www-form-urlencoded;34client_id=222222222-2222-2222-2222-222222222222&grant_type=client_credentials&client_secret=supersecureclientsecretstring&scope=FullAccess
1import { Auth } from "ordercloud-javascript-sdk";23Auth.ClientCredentials("supersecureclientsecret", "222222222-2222-2222-2222-222222222222", ["FullAccess"])4.then((token) => {5 console.log(token);6})7.catch((ex) => console.log(ex));
1import { Auth, AccessToken, OrderCloudError } from "ordercloud-javascript-sdk";23Auth.ClientCredentials("supersecureclientsecret", "222222222-2222-2222-2222-222222222222", ["FullAccess"])4.then((token: AccessToken) => {5 console.log(token);6})7.catch((ex: OrderCloudError) => console.log(ex));
1using OrderCloud.SDK;23try {4 AccessToken token = await ocClient.AuthenticateAsync("222222222-2222-2222-2222-222222222222", "supersecureclientsecret", new ApiRole[] { ApiRole.FullAccess });5 Console.WriteLine(token)6} catch(OrderCloudException ex) {7 Console.WriteLine(ex.Message);8}
Great job! You've now gone through the basics of setting up API Clients, Security Profiles and Authenticating users against the respective API Clients to get access tokens for making requests, you are unstoppable!
Considerations
This tutorial follows one implementation of the configuration of API Clients and Security Profiles. There are infinite ways to configure API Clients and Security Profiles to manage and control your data to fit your business needs. Hopefully this tutorial gives you a better understanding of the differences between API Clients and Security Profiles and how they can be used to build a powerful and secure e-commerce application suite.
Still have questions?
Ask in our Community Channel