From ba95ec683ce8d3d2202615db727b3820cb7a1c71 Mon Sep 17 00:00:00 2001 From: Jainam Tushar Sheth Date: Fri, 4 Oct 2024 02:00:37 -0400 Subject: [PATCH] Hybrid session sync fixes --- .../app/commerce-api/auth.js | 18 ++++--- .../app/commerce-api/constants.js | 3 +- .../app/commerce-api/utils.js | 47 +++++++++++++++++-- 3 files changed, 53 insertions(+), 15 deletions(-) diff --git a/packages/template-retail-react-app/app/commerce-api/auth.js b/packages/template-retail-react-app/app/commerce-api/auth.js index 0a3f44a598..8308db8f1e 100644 --- a/packages/template-retail-react-app/app/commerce-api/auth.js +++ b/packages/template-retail-react-app/app/commerce-api/auth.js @@ -22,7 +22,6 @@ import { REFRESH_TOKEN_COOKIE_AGE, EXPIRED_TOKEN, INVALID_TOKEN, - DWSID_STORAGE_KEY, EXCLUDE_COOKIE_SUFFIX } from './constants' import Cookies from 'js-cookie' @@ -51,7 +50,7 @@ class Auth { this._onClient = typeof window !== 'undefined' const _options = { - keySuffix: this._config.parameters.siteId, + keySuffix: this._config.parameters.siteId } this._cookieStorage = this._onClient ? new CookieStorage(_options) : new Map() @@ -97,7 +96,7 @@ class Auth { } get userType() { - return this._storage.get(refreshTokenRegisteredStorageKey) + return this._cookieStorage.get(refreshTokenRegisteredStorageKey) ? Auth.USER_TYPE.REGISTERED : Auth.USER_TYPE.GUEST } @@ -145,7 +144,7 @@ class Auth { } get dwsid() { - return this._cookieStorage.get(DWSID_STORAGE_KEY) + return this._cookieStorage.get(dwSessionIdKey) } get isTokenValid() { @@ -343,7 +342,7 @@ class Auth { // For Phased Launch storefronts, if the shopper logs into a registered account on PWA Kit, // dwsid cookie must be cleared to trigger onSession in Plugin SLAS which will then use the new // registered refresh_token (cc-nx cookie) value to restore SFRA/SG session for registered shopper. - this._cookieStorage.delete(DWSID_STORAGE_KEY) + this._cookieStorage.delete(dwSessionIdKey) const tokenBody = createGetTokenBody( response.url, @@ -475,13 +474,12 @@ class Auth { */ _clearAuth() { this._storage.delete(tokenStorageKey) - this._storage.delete(refreshTokenRegisteredStorageKey) - this._storage.delete(refreshTokenGuestStorageKey) - this._storage.delete(usidStorageKey) + this._cookieStorage.delete(refreshTokenRegisteredStorageKey) + this._cookieStorage.delete(refreshTokenGuestStorageKey) + this._cookieStorage.delete(usidStorageKey) this._storage.delete(cidStorageKey) this._storage.delete(encUserIdStorageKey) - this._storage.delete(dwSessionIdKey) - this._cookieStorage.delete(DWSID_STORAGE_KEY) + this._cookieStorage.delete(dwSessionIdKey) } } diff --git a/packages/template-retail-react-app/app/commerce-api/constants.js b/packages/template-retail-react-app/app/commerce-api/constants.js index 5527dcc0d7..36363bb998 100644 --- a/packages/template-retail-react-app/app/commerce-api/constants.js +++ b/packages/template-retail-react-app/app/commerce-api/constants.js @@ -16,10 +16,9 @@ export const dwSessionIdKey = 'dwsid' export const REFRESH_TOKEN_COOKIE_AGE = 90 // 90 days. This value matches SLAS cartridge. export const EXPIRED_TOKEN = 'EXPIRED_TOKEN' export const INVALID_TOKEN = 'invalid refresh_token' -export const DWSID_STORAGE_KEY = 'dwsid' export const ECOM_ACCESS_TOKEN_STORAGE_KEY = 'cc-at' export const DWSID_SERVER_AFFINITY_HEADER = 'sfdc_dwsid' // commerce-sdk-react namespaces cookies with siteID as suffixes to allow multisite setups. // However some cookies are set and used outside of PWA Kit and must not be modified with suffixes. -export const EXCLUDE_COOKIE_SUFFIX = ['dwsid'] \ No newline at end of file +export const EXCLUDE_COOKIE_SUFFIX = ['dwsid'] diff --git a/packages/template-retail-react-app/app/commerce-api/utils.js b/packages/template-retail-react-app/app/commerce-api/utils.js index 258e690428..3295525012 100644 --- a/packages/template-retail-react-app/app/commerce-api/utils.js +++ b/packages/template-retail-react-app/app/commerce-api/utils.js @@ -7,7 +7,12 @@ import jwtDecode from 'jwt-decode' import {getAppOrigin} from 'pwa-kit-react-sdk/utils/url' import {HTTPError} from 'pwa-kit-react-sdk/ssr/universal/errors' -import {ECOM_ACCESS_TOKEN_STORAGE_KEY, tokenStorageKey} from './constants' +import { + cidStorageKey, + ECOM_ACCESS_TOKEN_STORAGE_KEY, + tokenStorageKey, + usidStorageKey +} from './constants' import fetch from 'cross-fetch' /** @@ -273,6 +278,34 @@ export const convertSnakeCaseToSentenceCase = (text) => { */ export const noop = () => {} +/** + * Decode SLAS JWT and extract information such as customer id, usid, etc. + * @param {string} jwt + */ +export function parseSLASJwt(jwt) { + const payload = jwtDecode(jwt) + const {sub, isb} = payload + + if (!sub || !isb) { + throw new Error('Unable to parse access token payload: missing sub and isb.') + } + + // ISB format + //'uido:ecom::upn:Guest||xxxEmailxxx::uidn:FirstName LastName::gcid:xxxGuestCustomerIdxxx::rcid:xxxRegisteredCustomerIdxxx::chid:xxxSiteIdxxx', + const isbParts = isb.split('::') + const isGuest = isbParts[1] === 'upn:Guest' + const customerId = isGuest ? isbParts[3].replace('gcid:', '') : isbParts[4].replace('rcid:', '') + + // Sub format + // cc-slas::zzrf_001::scid:c9c45bfd-0ed3-4aa2-xxxx-40f88962b836::usid:b4865233-de92-4039-xxxx-aa2dfc8c1ea5 + const usid = sub.split('::')[3].replace('usid:', '') + return { + isGuest, + customerId, + usid + } +} + /** * WARNING: This function is relevant to be used in Phased Launch deployments only. * @@ -302,8 +335,16 @@ export function hasSFRAAuthStateChanged(storage, cookieStorage) { return true } - // If a cc-at cookie is found and it's value is NOT 'refresh', - // Update localStoreage token cookie to cc-at value and delete cc-at cookie. + /** + * If a cc-at cookie is found and it's value is NOT 'refresh', + * Parse JWT to get customerId and usid and update the values, + * Update localStoreage token cookie to cc-at value and delete cc-at cookie. + */ + const {customerId, usid} = parseSLASJwt(SFRAAuthToken) + storage.set(tokenStorageKey, `Bearer ${SFRAAuthToken}`) + storage.set(cidStorageKey, customerId) + cookieStorage.set(usidStorageKey, usid) cookieStorage.delete(ECOM_ACCESS_TOKEN_STORAGE_KEY) + return false }