Building a Purple Experience
Purchases, Entitlement & Coupo...
SSO-Manager
51 min
sso manager documentation overview the sso manager provides a unified authentication flow for both web and native app platforms it handles oauth and token based authentication with external sso providers, supporting multiple trigger mechanisms including url parameters and postmessage communication singleton pattern the ssomanager class is a singleton, ensuring only one authentication instance exists throughout the application lifecycle key features dual platform support works seamlessly on both web and native apps multiple trigger methods url parameters, postmessage (iframe integration) automatic state detection determines login/logout requirements based on sso provider responses configurable highly customizable through a central configuration file return url handling automatically manages return navigation after authentication singleton architecture built in singleton pattern ensures consistent state management file structure default/storefront/assets/scripts/custom/ ├── custom js # main entry point import init js here └── sso manager/ ├── config js # configuration file ├── sso manager js # core ssomanager class └── init js # extended class with lifecycle hooks installation & setup 1\ import in custom js add the import to your main custom js file file default/storefront/assets/scripts/custom/custom js import ' /sso manager/init js'; this will automatically initialize the sso manager when purple service is ready 2\ access the sso manager // import the singleton instance (from anywhere in your code) import { ssomanager } from ' /sso manager/init js'; // wait for initialization, then use ssomanager login(); // you can also use the base class directly if needed import { ssomanager } from ' /sso manager/sso manager js'; const instance = ssomanager getinstance(); important due to the singleton pattern, multiple instantiations return the same instance import { ssomanager } from ' /sso manager/sso manager js'; const instance1 = new ssomanager(); const instance2 = new ssomanager(); const instance3 = ssomanager getinstance(); console log(instance1 === instance2 === instance3); // true 3\ configure edit config js to match your environment file default/storefront/assets/scripts/custom/sso manager/config js export const config = { type "oauth", withpostmessage false, externalstate { login \["logged in", "true"], logout \["logged in", "false"], purchase \["purchase", "true"], token \["transfertoken"] }, urlparamkeys { returnurl "returnto", loginviaurl "external login", checkoutviaurl "external checkout", logoutviaurl "external logout", }, postmessage { validorigins \[ "https //dev hz de", "https //web purplemanager com/heidenheimer staging", "resource //dynamic", ], }, targeturlparams {}, returnurlparams {}, removeurlparams \["jwt"], loginurl "https //checkout stage hz de/dispatch", logouturl "https //checkout stage hz de/logout", checkouturl "https //checkout stage hz de/start", }; configuration reference core settings type type 'oauth' | 'transfertoken' description specifies which sso procedure is active example "oauth" withpostmessage type boolean description enable triggering login/logout via postmessage for iframe integration default false external state configuration the externalstate object defines url parameters that indicate authentication state from the sso provider structure each state is defined as an array \[urlparameterkey, expectedvalue?] if expectedvalue is provided, the parameter value must match exactly if expectedvalue is omitted, any value for that parameter is considered valid externalstate login type \[string, string?] description indicates external login state example \["logged in", "true"] checks if url has ?logged in=true externalstate logout type \[string, string?] description indicates external logout state example \["logged in", "false"] checks if url has ?logged in=false externalstate purchase type \[string, string?] description indicates external purchase action completed example \["purchase", "true"] checks if url has ?purchase=true externalstate token type \[string, string?] description transfertoken parameter, usually declared without expected value example \["transfertoken"] checks if url has ?transfertoken=\<any value> url parameter keys defines the url parameter names used to trigger and handle authentication flows urlparamkeys returnurl type string description url parameter name under which the external api expects the return url example "returnto" results in ?returnto=https //yoursite com urlparamkeys loginviaurl type string description url parameter which triggers login can receive a string value to use as targeturl example "external login" trigger with ?external login or ?external login=https //custom sso com urlparamkeys checkoutviaurl type string description url parameter which triggers checkout can receive a string value to use as targeturl example "external checkout" trigger with ?external checkout or ?external checkout=https //custom checkout com urlparamkeys logoutviaurl type string description url parameter which triggers logout example "external logout" trigger with ?external logout postmessage configuration postmessage validorigins type string\[] description array of origins allowed to trigger sso manager login/logout via postmessage security only messages from these origins will be processed example validorigins \[ "https //dev hz de", "https //web purplemanager com/heidenheimer staging", "resource //dynamic" // non standard protocol support ] additional url parameters targeturlparams type record\<string, string | number | boolean> description additional parameters to add to the target sso url example targeturlparams { client id "your client id", response type "token" } returnurlparams type record\<string, string | number | boolean> description additional parameters to add to the return url example returnurlparams { source "external auth", timestamp date now() } removeurlparams type string\[] description url parameters to remove upon login/logout to avoid endless loops or expose sensitive data example removeurlparams \["jwt", "session id", "temp token"] sso endpoint urls loginurl type string description url to be called for external login when none is provided in the login call example "https //checkout stage hz de/dispatch" logouturl type string description url to be called for logout (always used internally) example "https //checkout stage hz de/logout" checkouturl type string description url to be called for external checkout when none is provided in the checkout call example "https //checkout stage hz de/start" configuration examples basic oauth configuration export const config = { type "oauth", withpostmessage false, externalstate { login \["logged in", "true"], logout \["logged in", "false"], purchase \["purchase", "true"], token \["transfertoken"] }, urlparamkeys { returnurl "returnto", loginviaurl "external login", checkoutviaurl "external checkout", logoutviaurl "external logout", }, loginurl "https //sso example com/login", logouturl "https //sso example com/logout", checkouturl "https //sso example com/checkout", }; configuration with postmessage support export const config = { type "oauth", withpostmessage true, // enable postmessage externalstate { login \["auth status", "authenticated"], logout \["auth status", "logged out"], token \["access token"] }, urlparamkeys { returnurl "callback", loginviaurl "sso login", checkoutviaurl "sso checkout", logoutviaurl "sso logout", }, postmessage { validorigins \[ "https //paywall example com", "https //iframe app example com" ] }, targeturlparams { client id "abc123", scope "read write" }, returnurlparams { source "sso return" }, removeurlparams \["temp token", "session"], loginurl "https //auth example com/oauth/login", logouturl "https //auth example com/oauth/logout", checkouturl "https //auth example com/subscribe", }; transfer token configuration export const config = { type "transfertoken", withpostmessage false, externalstate { login \["status"], // any value accepted logout \["status"], // any value accepted token \["token"] // token key without specific value check }, urlparamkeys { returnurl "return url", loginviaurl "auth login", checkoutviaurl "auth checkout", logoutviaurl "auth logout", }, removeurlparams \["token", "status"], // clean sensitive data loginurl "https //auth example com/token login", logouturl "https //auth example com/token logout", checkouturl "https //auth example com/token checkout", }; usage examples 1\ trigger login via url basic login (uses default loginurl) https //www hz de?external login login with custom sso url https //www hz de?external login=https //custom sso com/login login from a specific page https //www hz de/premium article?external login after authentication, user returns to /premium article 2\ trigger checkout via url basic checkout https //www hz de?external checkout checkout with custom url https //www hz de?external checkout=https //custom checkout com/subscribe 3\ trigger logout via url https //www hz de?external logout 4\ programmatic login/logout // simple login ssomanager login(); // login with custom url ssomanager login('https //custom sso com/login'); // login with options ssomanager login('https //sso com/login', { params \[ \['promo', 'summer2024'], \['source', 'email'] ], returnurl 'https //www hz de/thank you' }); // checkout ssomanager checkout(); ssomanager checkout('https //custom checkout com'); // logout ssomanager logout(); 5\ postmessage integration (for iframes) enable in config withpostmessage true, postmessage { validorigins \[ "https //paywall iframe com" ] } from iframe // trigger login window\ parent postmessage({ method 'login', targeturl 'https //sso com/login' // optional }, ' '); // trigger checkout window\ parent postmessage({ method 'checkout', targeturl 'https //checkout com/start' // optional }, ' '); // trigger logout window\ parent postmessage({ method 'logout' }, ' '); authentication flow web platform flow 1\ user clicks login link └─> https //www hz de/article?external login 2\ script redirects to sso └─> https //sso example com/login?returnto=https //www hz de/article 3\ user authenticates on sso provider 4\ sso redirects back with auth params └─> https //www hz de/article?transfertoken=abc123\&logged in=true 5\ script processes authentication ├─> validates token parameter ├─> calls purple entitlement login() └─> cleans url https //www hz de/article 6\ page reloads with user logged in app platform flow 1\ user triggers login from app 2\ script calls purple app performauthentication() └─> native app handles oauth in system browser 3\ app receives auth callback 4\ script processes returned data ├─> validates authentication state └─> calls purple entitlement login() 5\ app refreshes with user logged in advanced configuration singleton pattern the ssomanager class uses a built in singleton pattern when you extend this class, the singleton behavior is automatically inherited // all of these return the same instance const instance1 = new ssomanager(); const instance2 = new ssomanager(); const instance3 = ssomanager getinstance(); console log(instance1 === instance2 === instance3); // true when you extend the class in init js, the extended class automatically inherits the singleton pattern class extendedssomanager extends ssomanager { // your custom implementation } const ext1 = new extendedssomanager(); const ext2 = new extendedssomanager(); console log(ext1 === ext2); // true singleton works! for testing import { ssomanager } from ' /sso manager/sso manager js'; // reset the singleton between tests beforeeach(() => { ssomanager resetinstance(); }); it('should initialize properly', () => { const instance = new ssomanager(); expect(instance) tobedefined(); }); custom lifecycle hooks extend the class in init js to add custom behavior the singleton pattern is automatically inherited no extra work needed file default/storefront/assets/scripts/custom/sso manager/init js import { ssomanager } from ' /sso manager'; import { extendstorefronthook } from " / /utils/extend storefront hook"; class customssomanager extends ssomanager { onlogin = { app async () => { // track login analytics analytics track('user logged in', { platform 'app' }); // clear local cache await clearcache(); }, web async () => { // track login analytics analytics track('user logged in', { platform 'web' }); // update ui updateuserinterface(); } } onlogout = { app async () => { // clear user data await clearuserdata(); }, web async () => { // reset session sessionstorage clear(); } } } // automatic initialization via hook export let ssomanager = null; extendstorefronthook('onpurpleserviceinit', () => { if (!ssomanager) { ssomanager = new customssomanager(); } }); that's it! the singleton pattern is inherited from the parent class, and initialization happens automatically when purple service initializes adding parameters to target url targeturlparams { client id "your client id", response type "token", scope "read write" } result sso url will include these params automatically adding parameters to return url returnurlparams { source "external auth", timestamp date now() } result return url will include these params removing sensitive parameters removeurlparams \[ "jwt", "session id", "temp token" ] these parameters are removed after authentication to keep urls clean api reference singleton methods ssomanager getinstance() get the singleton instance creates a new instance if none exists returns ssomanager the singleton instance example import { ssomanager } from ' /sso manager/sso manager js'; const auth = ssomanager getinstance(); auth login(); ssomanager resetinstance() reset the singleton instance useful for testing or reinitialization returns void example import { ssomanager } from ' /sso manager/sso manager js'; // for testing purposes ssomanager resetinstance(); const newinstance = new ssomanager(); initialization functions the sso manager uses automatic initialization via purple service hooks // in custom js import to activate import ' /sso manager/init js'; // in init js automatic initialization import { ssomanager } from ' /sso manager/init js'; extendstorefronthook('onpurpleserviceinit', () => { if (!ssomanager) { ssomanager = new extendedssomanager(); } }); no manual initialization required the singleton is created automatically when purple service initializes direct access if you need to access the singleton directly without waiting for the hook import { ssomanager } from ' /sso manager/sso manager js'; const instance = ssomanager getinstance(); public methods login(targeturl?, options?) trigger the login flow parameters targeturl (string, optional) sso login url uses config loginurl if omitted options (object, optional) params (array) additional url parameters as \[key, value] pairs returnurl (string) custom return url for this login example ssomanager login('https //sso com/login', { params \[\['promo', 'welcome']], returnurl 'https //www hz de/success' }); checkout(targeturl?, options?) trigger the checkout flow same signature as login() example ssomanager checkout('https //checkout com/start'); logout() trigger the logout flow example ssomanager logout(); public properties config access the configuration object console log(ssomanager config loginurl); ssomanager config loginurl = 'https //new sso com/login'; isweb boolean indicating if running on web platform if (ssomanager isweb) { console log('running on web'); } utils utility functions for url handling // clear url parameters ssomanager utils clearurlparams(); // get return url const returnurl = ssomanager utils getreturnurl(); // get stripped url (without auth params) const cleanurl = ssomanager utils getstrippedurl(); troubleshooting singleton related issues problem configuration changes don't take effect solution the singleton instance is created with the initial configuration to change configuration import { ssomanager } from ' /sso manager/sso manager js'; // option 1 reset and recreate ssomanager resetinstance(); const newinstance = new ssomanager(); // option 2 modify existing instance config (runtime changes) const instance = ssomanager getinstance(); instance config loginurl = 'https //new url com'; problem different parts of the app have different authentication states solution this shouldn't happen with the singleton pattern if it does, verify that you're not accidentally calling resetinstance() somewhere all parts of your app import from the same module path no bundler/module issues are creating duplicate instances all instantiations should go through the same singleton login not triggered problem clicking login link doesn't redirect to sso solutions check if loginurl is set in config verify url parameter matches loginviaurl in config check browser console for errors infinite redirect loop problem page keeps redirecting to sso and back solutions verify externalstate configuration matches sso parameters add problematic parameters to removeurlparams array check that sso is sending expected parameter names/values postmessage not working problem iframe postmessage doesn't trigger login solutions enable postmessage withpostmessage true add iframe origin to validorigins array check browser console for origin validation errors for non standard protocols (resource //), ensure url format is correct return url issues problem user returns to wrong page after authentication solutions check returnurl parameter name matches sso expectation verify urlparamkeys returnurl is configured correctly use custom return url in login options if needed app authentication fails problem authentication doesn't work in native app solutions verify purple app performauthentication is available check callbackparamname matches app configuration ensure app has proper url scheme registration security considerations postmessage origin validation always specify exact origins in validorigins postmessage { validorigins \[ "https //trusted iframe com", "https //paywall example com" ] } never use wildcards in production! token handling tokens are passed via url parameters and immediately processed urls are cleaned after token extraction using removeurlparams tokens should be short lived (< 5 minutes) https only always use https urls for sso endpoints in production loginurl "https //sso example com/login" // ✅ good loginurl "http //sso example com/login" // ❌ bad browser support modern browsers chrome 90+, firefox 88+, safari 14+, edge 90+ mobile ios safari 14+, chrome mobile 90+ required apis url / urlsearchparams window\ history replacestate window\ addeventlistener (postmessage) promises / async await support for issues or questions check console logs (debug level) verify configuration matches sso provider requirements review authentication flow in network tab contact your sso provider for server side issues