Unified Identity Management in Hybrid Composable Solutions

Empowering Browsers: Seamless Requests to Controllers and APIs for a Consistent Shopper Experience

Oleg Sapishchuk
7 min readNov 23, 2023

Disclaimer: This article is based on the original work of John Boxall, with contributions from Sandra Golden, Holger Nestmann, and others.

Image by angelitrona

Today’s article serves as an unofficial guide to Salesforce B2C Commerce Cloud, focusing on the utilization of SCAPI/OCAPI and Controllers. The aim is to outline an approach that customers and partners can adopt to maintain synchronization between these mechanisms while constructing custom sites. In essence, it ensures that, at any given time, a browser can seamlessly make a request to a controller or an API as the same shopper.

Our ecosystem experiences increased demand when customers are building headless sites that interact both with our APIs (SCAPI/OCAPI) and controllers. These different methods of interacting with the platform have separate authentication mechanisms:

  • APIs use JWT access tokens, either from SLAS our OCAPI Shop /customers/auth

Access tokens are good for 30 minutes. Refresh tokens provided by SLAS can be used to get a new access token for up to 90 days.

  • Controllers use sessions which are powered by HTTP cookies prefixed with dw (DW cookies)

Sessions are good for 30 minutes and extended when a request is made to a controller with a session. After 30 minutes, they expire. Sessions can be extended for up to six hours which is configurable to lower values.

Managing the lifecycles of these two mechanisms is complex, and messing it up can cause problems.

New Sessions (New Guests)

The process of establishing the session is different depending on whether the shopper landed on a headless page (let’s call it “Head) powered by APIs(e.g. PWA+BFF) or a controller-based page, which can also known as “headed” (e.g. SFRA/SG).

Before we deep dive to solutions, let me also clarify another term that you may heard before, it is “ECOM”. “ECOM” in the context of this article is the same as “Headed”, as it talks about controller/pipeline-based implementations on B2C Commerce Cloud.

Landing on a Headless Site (“Head”)

The following sequence of API calls and actions would need to be performed:

  1. Call SLAS authorizeCustomer (oauth2/authorize)
  2. Call SLAS getAccessToken (oauth2/token)
  3. If you use a SLAS private client, you only need to call getAccessToken.
  4. At this point, you’ve got a token response from SLAS. This includes a JWT you can use to access the APIs and a refresh token you can use to get a new JWT. Persist the refresh token on the browser to allow you to get new access tokens or DW cookies for the shopper in the future.
  5. Call OCAPI Shop /sessions

Congratulations! At this point, you’re ready to make requests either to our APIs or Controllers!

%% New guest session, starting on the PWA with a Private Client. sequenceDiagram autonumber Browser->>BFF: GET / BFF->>SLAS: POST ../oauth2/token SLAS->>BFF: 200 OK<br>{$refresh, $access} BFF->>OCAPI: POST /sessions<br>Authorization: Bearer $access OCAPI->>BFF: 200 OK<br>Set-Cookie: $dwsid BFF->>Browser: 200 OK<br>Set-Cookie: $dwsid, $refresh, $access

Landing on Headed Site (SFRA/SG)

Consider using Plugin SLAS for this, but if you must do it yourself…

  1. In this scenario, ECOM creates DW cookies for us, so in this flow, we only need to get an SLAS/access refresh token so we can call APIs and return to this shopper later.
  2. Call SLAS authorizeCustomer (oauth2/authorize)
  3. Call SLAS getSessionBridgeAccessToken (oauth2/session-bridge/token)
  4. At this point, you’ve got a token response from SLAS. You need to store it in such a way that your “Head” can access it.

🎉 You’re ready to make requests either to our APIs or Controllers!

%% New guest session, starting on SFRA/SG with a Private Client. sequenceDiagram autonumber Browser->>ECOM: GET / ECOM->>SLAS: POST ../oauth2/session-bridge/token<br>{$dwsid} SLAS->>ECOM: 200 OK<br>{$refresh, $access} ECOM->>Browser: 200 OK<br>Set-Cookie: $dwsid, $refresh, $access

In Salesforce Commerce Composable Solution, for PWA Kit to work correctly above translates into:

1. the access token as token

2. the customer id as cid

3. the refresh token as either cc-nx-g or cc-nx

4. the unique shopper id usid

Returning Visitors

Returning guest or registered shopper flows aren’t different, but the sequence of calls again depends on whether you landed first on “ECOM” or the “Head”.

Returning on the “Head”

Assuming you stored the SLAS refresh token in a cookie then …

  1. Use the refresh token to call SLAS getAccessToken (oauth2/token?refresh_token=...)
  2. Call OCAPI Shop /sessions to get new DW cookies for the shopper.

🎉 You’re again ready to make requests either to our APIs or Controllers!

%% Returning Visitor, starting on PWA with a Private Client. sequenceDiagram autonumber Browser->>BFF: GET /<br>Cookie: $refresh BFF->>SLAS: POST ../oauth2/token?grant_type=refresh_token<br>{$refresh} SLAS->>BFF: 200 OK<br>{$refresh, $access} BFF->>OCAPI: POST /sessions<br>Authorization: Bearer $access OCAPI->>BFF: 200 OK<br>Set-Cookie: $dwsid BFF->>Browser: 200 OK<br>Set-Cookie: $dwsid, $refresh, $access

Returning Visitor on SFRA

Once again, this is hard! So ideally you would be using Plugin SLAS for this, but if you must do it yourself…

  1. Use the refresh token to call SLAS getAccessToken (oauth2/token?refresh_token=...)
  2. Call OCAPI Shop /sessions to get new DW cookies for the shopper.
  3. Refresh the page.

Yes! Above point may sounds really weird, but if you don’t, then visitors will have trouble submitting forms, as the page was rendered in the context of different DW cookies.

🎉 Finally you are again ready to make requests either to our APIs or Controllers!

%% Return visitor, starting on SFRA/SG with a Private Client. sequenceDiagram autonumber Browser->>ECOM: GET /<br>Cookie: $refresh ECOM->>SLAS: POST ../oauth2/token?grant_type=refresh_token<br>{$refresh} SLAS->>ECOM: 200 OK<br>{$refresh, $access} ECOM->>OCAPI: POST /sessions<br>Authorization: Bearer $access OCAPI->>ECOM: 200 OK<br>Set-Cookie: $dwsid ECOM->>Browser: 302 Redirect<br>Location: .<br>Set-Cookie: $dwsid, $refresh, $access

To exercise all 4 aforementioned scenarios John also created a gist with bash scripts. It will be vital for you to better understand how it all works in practice.

Additional Scenarios

On top of the covered use cases, there will be other scenarios that you need to take care of in your solution design and after during the implementation. Which we will cover next.

SLAS Access Token timeout

Access tokens are good for 30 minutes from when they are granted. You can decode the JWT and look at the exp field for the exact timestamp. Once 30 minutes elapse, the token can no longer be used to make an API request.

  • Use the refresh token to call SLAS getAccessToken (oauth2/token?grant_type=refresh_token) to get a new token.

Refresh tokens from public clients can only be redeemed once, while refresh tokens from private clients can be used for multiple days.

For production instances, refresh tokens are good for 90 days. For other instances, refresh tokens are good for 9 days.

DW session timeout

A DW session is valid for 30 minutes. After 30 minutes of no activity (that is no requests to controllers where it is used) it will expire.

  • You can check whether given DW session is valid using OCAPI Shop /customers/auth?type=session. This endpoint will return a fault if the DW session has expired.

If the session expired:

  • get a new one by calling OCAPI Shop /sessions with a valid SLAS access token.

You need to be careful because if you make a request to a controller with an expired session, you won’t get an error. You’ll get a new guest session and a response in the context of that session.

Login and Logout

  1. When shoppers login, both the access token/refresh token and DW cookies need to be updated.
  2. When shoppers logout, both the access token/refresh token and DW cookies need to be cleared.

In Salesforce Commerce Composable Solution, for PWA Kit above translates into the full list of following attributes, while removing those on logout:

1. the access token as token

2. the customer id as cid

3. the refresh token as either cc-nx-g or cc-nx

4. the unique shopper id usid

Other pointers and highlights

In addition to the main content of the article, the following ideas and notes may be worth your attention.

Basket & Bad Ideas

Don’t create a basket until you will add something to it. Hence, don’t create a basket using SCAPI or OCAPI until you are adding something to it. Also, stop calling BasketMgr.getCurrentOrNewBasket().

If you do create “empty” baskets, the following scenario may occur:

  • The shopper arrives on “Head” and you create an “empty” basket. Because the basket is empty, it is not synchronized with “ECOM”.
  • Shopper navigates to “ECOM”-driven Cart page which used BasketMgr.getCurrentOrNewBasket(). Another basket is created on another App Server in this case.
  • Later Shopper adds a product to the basket on “Head”. When the shopper navigates to the Cart (which is driven by the “ECOM” experience), the Basket will be empty. It will be empty, as the App Server will load the in-memory basket that is empty.

So… don’t create a basket until you add something to it!

With that, in addition to the flows we covered before, you need to call mergeBasket, only if your shopper has items in the basket. You would mergeBasket call after you retrieve the token from SLAS and before you call the OCAPI Shop Sessions endpoint.

Plugin SLAS and Custom “Head”s

Long story short you may want to integrate Plugin SLAS with your custom “Head”. For this, your headless app needs to set the cookies that Plugin SLAS expects, specifically:

  • For guest shoppers, place a SLAS refresh token in cc-nx-g
  • For the registered shopper, place an SLAS refresh token in cc-nx

Helpful resources

  1. Commerce API reference docs
  2. Node.js SDK for Commerce API
  3. Javascript/browser SDK for Commerce API
  4. Plugin SLAS
  5. John’s Gist
  6. John’s original Quip
photo by Sam Spicer

My name is Oleg Sapishchuk, and I’m an experienced architect providing digital transformation with unified commerce solutions for some of the world’s best-known and most influential brands. Salesforce B2C Commerce is the topic that I write about most frequently. If you are interested in new or previously written material, I invite you to follow my Medium profile.

--

--

Oleg Sapishchuk

Digital leader driving transformation and unified commerce solutions for global brands. Passionate about innovation, tech strategy, and customer success.