@pega/auth
Advanced tools
Comparing version 0.2.16 to 0.2.17
@@ -41,2 +41,4 @@ // This file wraps various calls related to logging in, logging out, etc. | ||
#loginProps = null; | ||
// Promise for Initialization of PegaAuth and resolution callback | ||
#promiseInitialize = null; | ||
constructor() { | ||
@@ -72,14 +74,27 @@ // Auth Manager specific state is saved within session storage as important in redirect and popup window scenarios | ||
// helper routine to set storage to the passed in JSON | ||
#setStorage(ssKey, obj) { | ||
// Set storage only if obj is not empty, else delete the storage | ||
if (!obj || isEmptyObject(obj)) { | ||
window.sessionStorage.removeItem(ssKey); | ||
#setStorage(ssKey, obj, bVerify = false) { | ||
try { | ||
// Set storage only if obj is not empty, else delete the storage | ||
if (!obj || isEmptyObject(obj)) { | ||
window.sessionStorage.removeItem(ssKey); | ||
} | ||
else { | ||
// const bClear = (ssKey === this.#ssKeyState || ssKey === this.#ssKeySessionInfo); | ||
const bClear = false; | ||
const sValue = bClear | ||
? JSON.stringify(obj) | ||
: this.#transformer(ssKey, JSON.stringify(obj), true); | ||
window.sessionStorage.setItem(ssKey, sValue); | ||
if (bVerify) { | ||
const sStored = window.sessionStorage.getItem(ssKey); | ||
if (sStored !== sValue) { | ||
// eslint-disable-next-line no-console | ||
console.error(`Failed to properly write sessionStorage (${ssKey})`); | ||
} | ||
} | ||
} | ||
} | ||
else { | ||
// const bClear = (ssKey === this.#ssKeyState || ssKey === this.#ssKeySessionInfo); | ||
const bClear = false; | ||
const sValue = bClear | ||
? JSON.stringify(obj) | ||
: this.#transformer(ssKey, JSON.stringify(obj), true); | ||
window.sessionStorage.setItem(ssKey, sValue); | ||
catch (e) { | ||
// eslint-disable-next-line no-console | ||
console.error(`Exception ${e} on attempted write of sessionStorage (${ssKey})`); | ||
} | ||
@@ -264,182 +279,172 @@ } | ||
async #initialize(bNew = false) { | ||
return new Promise(resolve => { | ||
if (!this.initInProgress && (bNew || isEmptyObject(this.#authConfig) || !this.#pegaAuth)) { | ||
this.initInProgress = true; | ||
getSdkConfig().then(sdkConfig => { | ||
const sdkConfigAuth = sdkConfig.authConfig; | ||
const sdkConfigServer = sdkConfig.serverConfig; | ||
const serverType = sdkConfigServer.serverType || 'infinity'; | ||
const bInfinity = serverType === 'infinity'; | ||
let pegaUrl = bInfinity | ||
? sdkConfigServer.infinityRestServerUrl | ||
: sdkConfigServer.launchpadRestServerUrl; | ||
// Expecting boolean true/false or undefined | ||
const secureCookie = !!sdkConfigAuth.secureCookie; | ||
const bNoInitialRedirect = this.noInitialRedirect; | ||
const appAliasSeg = sdkConfigServer.appAlias ? `app/${sdkConfigServer.appAlias}/` : ''; | ||
// Construct default OAuth endpoints (if not explicitly specified) | ||
if (pegaUrl) { | ||
// Cope with trailing slash being present | ||
if (!pegaUrl.endsWith('/')) { | ||
pegaUrl += '/'; | ||
} | ||
if (!sdkConfigAuth.authorize) { | ||
sdkConfigAuth.authorize = bInfinity | ||
? `${pegaUrl}PRRestService/oauth2/v1/authorize` | ||
: `${pegaUrl}uas/oauth/authorize`; | ||
} | ||
const infinityOAuth2Url = bInfinity | ||
? pegaUrl + (secureCookie && appAliasSeg ? `${appAliasSeg}api/` : 'PRRestService/') | ||
: ''; | ||
if (!sdkConfigAuth.token) { | ||
sdkConfigAuth.token = bInfinity | ||
? `${infinityOAuth2Url}oauth2/v1/token` | ||
: `${pegaUrl}uas/oauth/token`; | ||
} | ||
if (!sdkConfigAuth.revoke) { | ||
// Launchpad still does not have a revoke endpoint | ||
sdkConfigAuth.revoke = bInfinity ? `${infinityOAuth2Url}oauth2/v1/revoke` : ''; | ||
} | ||
if (!sdkConfigAuth.redirectUri) { | ||
sdkConfigAuth.redirectUri = `${window.location.origin}${window.location.pathname}`; | ||
} | ||
if (!sdkConfigAuth.userinfo) { | ||
sdkConfigAuth.userinfo = bInfinity | ||
? `${pegaUrl}${appAliasSeg}api/oauthclients/v1/userinfo/JSON` | ||
: ''; | ||
} | ||
if (!bNew && this.#promiseInitialize) { | ||
return this.#promiseInitialize; | ||
} | ||
this.#promiseInitialize = new Promise(resolve => { | ||
/* if (!this.initInProgress && (bNew || isEmptyObject(this.#authConfig) || !this.#pegaAuth)) { */ | ||
this.initInProgress = true; | ||
getSdkConfig().then(sdkConfig => { | ||
const sdkConfigAuth = sdkConfig.authConfig; | ||
const sdkConfigServer = sdkConfig.serverConfig; | ||
const serverType = sdkConfigServer.serverType || 'infinity'; | ||
const bInfinity = serverType === 'infinity'; | ||
let pegaUrl = bInfinity | ||
? sdkConfigServer.infinityRestServerUrl | ||
: sdkConfigServer.launchpadRestServerUrl; | ||
// Expecting boolean true/false or undefined | ||
const secureCookie = !!sdkConfigAuth.secureCookie; | ||
const bNoInitialRedirect = this.noInitialRedirect; | ||
const appAliasSeg = sdkConfigServer.appAlias ? `app/${sdkConfigServer.appAlias}/` : ''; | ||
// Construct default OAuth endpoints (if not explicitly specified) | ||
if (pegaUrl) { | ||
// Cope with trailing slash being present | ||
if (!pegaUrl.endsWith('/')) { | ||
pegaUrl += '/'; | ||
} | ||
// Auth service alias | ||
if (!sdkConfigAuth.authService) { | ||
sdkConfigAuth.authService = 'pega'; | ||
if (!sdkConfigAuth.authorize) { | ||
sdkConfigAuth.authorize = bInfinity | ||
? `${pegaUrl}PRRestService/oauth2/v1/authorize` | ||
: `${pegaUrl}uas/oauth/authorize`; | ||
} | ||
// mashupAuthService provides way to have a different auth service for embedded | ||
if (!sdkConfigAuth.mashupAuthService) { | ||
sdkConfigAuth.mashupAuthService = sdkConfigAuth.authService; | ||
const infinityOAuth2Url = bInfinity | ||
? pegaUrl + (secureCookie && appAliasSeg ? `${appAliasSeg}api/` : 'PRRestService/') | ||
: ''; | ||
if (!sdkConfigAuth.token) { | ||
sdkConfigAuth.token = bInfinity | ||
? `${infinityOAuth2Url}oauth2/v1/token` | ||
: `${pegaUrl}uas/oauth/token`; | ||
} | ||
// Construct path to auth.html (used for case when not doing a main window redirect) | ||
let sNoMainRedirectUri = sdkConfigAuth.redirectUri; | ||
const nLastPathSep = sNoMainRedirectUri.lastIndexOf('/'); | ||
sNoMainRedirectUri = | ||
nLastPathSep !== -1 | ||
? `${sNoMainRedirectUri.substring(0, nLastPathSep + 1)}auth.html` | ||
: `${sNoMainRedirectUri}/auth.html`; | ||
const portalGrantType = sdkConfigAuth.portalGrantType || 'authCode'; | ||
const mashupGrantType = sdkConfigAuth.mashupGrantType || 'authCode'; | ||
// Some grant types are only available with confidential registrations and require a client secret | ||
const clientSecret = bNoInitialRedirect | ||
? sdkConfigAuth.mashupClientSecret | ||
: sdkConfigAuth.portalClientSecret; | ||
const pegaAuthConfig = { | ||
serverType, | ||
clientId: bNoInitialRedirect | ||
? sdkConfigAuth.mashupClientId | ||
: sdkConfigAuth.portalClientId, | ||
grantType: bNoInitialRedirect ? mashupGrantType : portalGrantType, | ||
tokenUri: sdkConfigAuth.token, | ||
revokeUri: sdkConfigAuth.revoke, | ||
userinfoUri: sdkConfigAuth.userinfo, | ||
authService: bNoInitialRedirect | ||
? sdkConfigAuth.mashupAuthService | ||
: sdkConfigAuth.authService, | ||
appAlias: sdkConfigServer.appAlias || '', | ||
useLocking: true | ||
}; | ||
if (clientSecret) { | ||
pegaAuthConfig.clientSecret = clientSecret; | ||
if (!sdkConfigAuth.revoke) { | ||
// Launchpad still does not have a revoke endpoint | ||
sdkConfigAuth.revoke = bInfinity ? `${infinityOAuth2Url}oauth2/v1/revoke` : ''; | ||
} | ||
if (serverType === 'launchpad' && pegaAuthConfig.grantType === 'authCode') { | ||
pegaAuthConfig.noPKCE = true; | ||
if (!sdkConfigAuth.redirectUri) { | ||
sdkConfigAuth.redirectUri = `${window.location.origin}${window.location.pathname}`; | ||
} | ||
// Invoke keySuffix setter | ||
// Was using pegaAuthConfig.clientId as key but more secure to just use a random string as getting | ||
// both a clientId and the refresh token could yield a new access token. | ||
// Suffix is so we might in future move to an array of suffixes based on the appName, so might store | ||
// both portal and embedded tokens/session info at same time | ||
if (!this.state?.sfx) { | ||
// Just using a random number to make the suffix unique on each session | ||
this.keySuffix = `${Math.ceil(Math.random() * 100000000)}`; | ||
if (!sdkConfigAuth.userinfo) { | ||
sdkConfigAuth.userinfo = bInfinity | ||
? `${pegaUrl}${appAliasSeg}api/oauthclients/v1/userinfo/JSON` | ||
: ''; | ||
} | ||
this.#authConfig.transform = | ||
sdkConfigAuth.transform !== undefined ? sdkConfigAuth.transform : this.#transform; | ||
// Using property in class as authConfig may be empty at times | ||
this.#transform = this.#authConfig.transform; | ||
if (sdkConfigAuth.tokenStorage !== undefined) { | ||
this.#tokenStorage = sdkConfigAuth.tokenStorage; | ||
} | ||
// Auth service alias | ||
if (!sdkConfigAuth.authService) { | ||
sdkConfigAuth.authService = 'pega'; | ||
} | ||
// mashupAuthService provides way to have a different auth service for embedded | ||
if (!sdkConfigAuth.mashupAuthService) { | ||
sdkConfigAuth.mashupAuthService = sdkConfigAuth.authService; | ||
} | ||
// Construct path to auth.html (used for case when not doing a main window redirect) | ||
let sNoMainRedirectUri = sdkConfigAuth.redirectUri; | ||
const nLastPathSep = sNoMainRedirectUri.lastIndexOf('/'); | ||
sNoMainRedirectUri = | ||
nLastPathSep !== -1 | ||
? `${sNoMainRedirectUri.substring(0, nLastPathSep + 1)}auth.html` | ||
: `${sNoMainRedirectUri}/auth.html`; | ||
const portalGrantType = sdkConfigAuth.portalGrantType || 'authCode'; | ||
const mashupGrantType = sdkConfigAuth.mashupGrantType || 'authCode'; | ||
// Some grant types are only available with confidential registrations and require a client secret | ||
const clientSecret = bNoInitialRedirect | ||
? sdkConfigAuth.mashupClientSecret | ||
: sdkConfigAuth.portalClientSecret; | ||
const pegaAuthConfig = { | ||
serverType, | ||
clientId: bNoInitialRedirect | ||
? sdkConfigAuth.mashupClientId | ||
: sdkConfigAuth.portalClientId, | ||
grantType: bNoInitialRedirect ? mashupGrantType : portalGrantType, | ||
tokenUri: sdkConfigAuth.token, | ||
revokeUri: sdkConfigAuth.revoke, | ||
userinfoUri: sdkConfigAuth.userinfo, | ||
authService: bNoInitialRedirect | ||
? sdkConfigAuth.mashupAuthService | ||
: sdkConfigAuth.authService, | ||
appAlias: sdkConfigServer.appAlias || '', | ||
useLocking: true | ||
}; | ||
if (clientSecret) { | ||
pegaAuthConfig.clientSecret = clientSecret; | ||
} | ||
if (serverType === 'launchpad' && pegaAuthConfig.grantType === 'authCode') { | ||
pegaAuthConfig.noPKCE = true; | ||
} | ||
// Invoke keySuffix setter | ||
// Was using pegaAuthConfig.clientId as key but more secure to just use a random string as getting | ||
// both a clientId and the refresh token could yield a new access token. | ||
// Suffix is so we might in future move to an array of suffixes based on the appName, so might store | ||
// both portal and embedded tokens/session info at same time | ||
if (!this.state?.sfx) { | ||
// Just using a random number to make the suffix unique on each session | ||
this.keySuffix = `${Math.ceil(Math.random() * 100000000)}`; | ||
} | ||
this.#authConfig.transform = | ||
sdkConfigAuth.transform !== undefined ? sdkConfigAuth.transform : this.#transform; | ||
// Using property in class as authConfig may be empty at times | ||
this.#transform = this.#authConfig.transform; | ||
if (sdkConfigAuth.tokenStorage !== undefined) { | ||
this.#tokenStorage = sdkConfigAuth.tokenStorage; | ||
} | ||
if (sdkConfigAuth.secureCookie) { | ||
this.#authConfig.secureCookie = true; | ||
} | ||
// Get latest state once client ids, transform and tokenStorage have been established | ||
this.#doOnLoad(); | ||
// If no clientId is specified assume not OAuth but custom auth | ||
if (pegaAuthConfig.grantType === 'none' || !pegaAuthConfig.clientId) { | ||
this.bCustomAuth = true; | ||
return; | ||
} | ||
if (pegaAuthConfig.grantType === 'authCode') { | ||
const authCodeProps = { | ||
authorizeUri: sdkConfigAuth.authorize, | ||
// If we have already specified a redirect on the authorize redirect, we need to continue to use that | ||
// on token endpoint | ||
redirectUri: bNoInitialRedirect || this.usePopupForRestOfSession | ||
? sNoMainRedirectUri | ||
: sdkConfigAuth.redirectUri | ||
}; | ||
if ('silentTimeout' in sdkConfigAuth) { | ||
authCodeProps.silentTimeout = sdkConfigAuth.silentTimeout; | ||
} | ||
if (sdkConfigAuth.secureCookie) { | ||
this.#authConfig.secureCookie = true; | ||
if (bNoInitialRedirect && | ||
pegaAuthConfig.authService === 'pega' && | ||
sdkConfigAuth.mashupUserIdentifier && | ||
sdkConfigAuth.mashupPassword) { | ||
authCodeProps.userIdentifier = sdkConfigAuth.mashupUserIdentifier; | ||
authCodeProps.password = sdkConfigAuth.mashupPassword; | ||
} | ||
// Get latest state once client ids, transform and tokenStorage have been established | ||
this.#doOnLoad(); | ||
// If no clientId is specified assume not OAuth but custom auth | ||
if (pegaAuthConfig.grantType === 'none' || !pegaAuthConfig.clientId) { | ||
this.bCustomAuth = true; | ||
return; | ||
if ('iframeLoginUI' in sdkConfigAuth) { | ||
authCodeProps.iframeLoginUI = | ||
sdkConfigAuth.iframeLoginUI.toString().toLowerCase() === 'true'; | ||
} | ||
if (pegaAuthConfig.grantType === 'authCode') { | ||
const authCodeProps = { | ||
authorizeUri: sdkConfigAuth.authorize, | ||
// If we have already specified a redirect on the authorize redirect, we need to continue to use that | ||
// on token endpoint | ||
redirectUri: bNoInitialRedirect || this.usePopupForRestOfSession | ||
? sNoMainRedirectUri | ||
: sdkConfigAuth.redirectUri | ||
}; | ||
if ('silentTimeout' in sdkConfigAuth) { | ||
authCodeProps.silentTimeout = sdkConfigAuth.silentTimeout; | ||
} | ||
if (bNoInitialRedirect && | ||
pegaAuthConfig.authService === 'pega' && | ||
sdkConfigAuth.mashupUserIdentifier && | ||
sdkConfigAuth.mashupPassword) { | ||
authCodeProps.userIdentifier = sdkConfigAuth.mashupUserIdentifier; | ||
authCodeProps.password = sdkConfigAuth.mashupPassword; | ||
} | ||
if ('iframeLoginUI' in sdkConfigAuth) { | ||
authCodeProps.iframeLoginUI = | ||
sdkConfigAuth.iframeLoginUI.toString().toLowerCase() === 'true'; | ||
} | ||
Object.assign(pegaAuthConfig, authCodeProps); | ||
} | ||
else if (pegaAuthConfig.grantType === 'passwordCreds') { | ||
pegaAuthConfig.userIdentifier = sdkConfigAuth.mashupUserIdentifier; | ||
pegaAuthConfig.password = sdkConfigAuth.mashupPassword; | ||
} | ||
Object.assign(this.#authConfig, pegaAuthConfig); | ||
// Add beforeunload and page hide handlers to write out key properties that we want to survive a | ||
// browser reload | ||
if (!this.#beforeUnloadAdded && (!this.#usePASS || this.#tokenStorage !== 'always')) { | ||
window.addEventListener('beforeunload', this.#doBeforeUnload.bind(this)); | ||
window.addEventListener('pagehide', this.#doPageHide.bind(this)); | ||
this.#beforeUnloadAdded = true; | ||
} | ||
// Initialize PegaAuth OAuth 2.0 client library | ||
if (this.#usePASS) { | ||
this.#setStorage(this.#ssKeyConfigInfo, this.#authConfig); | ||
this.#setStorage(this.#ssKeySessionInfo, this.#authDynState); | ||
this.#pegaAuth = new PegaAuth(this.#ssKeyConfigInfo, this.#ssKeySessionInfo); | ||
} | ||
else { | ||
this.#authConfig.fnDynStateChangedCB = this.#doAuthDynStateChanged.bind(this); | ||
this.#pegaAuth = new PegaAuth(this.#authConfig, this.#authDynState); | ||
} | ||
this.initInProgress = false; | ||
resolve(this.#pegaAuth); | ||
}); | ||
} | ||
else { | ||
let idNextCheck; | ||
const fnCheckForAuthMgr = () => { | ||
if (!this.initInProgress) { | ||
if (idNextCheck) { | ||
clearInterval(idNextCheck); | ||
} | ||
resolve(this.#pegaAuth); | ||
} | ||
}; | ||
fnCheckForAuthMgr(); | ||
idNextCheck = setInterval(fnCheckForAuthMgr, 100); | ||
} | ||
Object.assign(pegaAuthConfig, authCodeProps); | ||
} | ||
else if (pegaAuthConfig.grantType === 'passwordCreds') { | ||
pegaAuthConfig.userIdentifier = sdkConfigAuth.mashupUserIdentifier; | ||
pegaAuthConfig.password = sdkConfigAuth.mashupPassword; | ||
} | ||
Object.assign(this.#authConfig, pegaAuthConfig); | ||
// Add beforeunload and page hide handlers to write out key properties that we want to survive a | ||
// browser reload | ||
if (!this.#beforeUnloadAdded && (!this.#usePASS || this.#tokenStorage !== 'always')) { | ||
window.addEventListener('beforeunload', this.#doBeforeUnload.bind(this)); | ||
window.addEventListener('pagehide', this.#doPageHide.bind(this)); | ||
this.#beforeUnloadAdded = true; | ||
} | ||
// Initialize PegaAuth OAuth 2.0 client library | ||
if (this.#usePASS) { | ||
this.#setStorage(this.#ssKeyConfigInfo, this.#authConfig); | ||
this.#setStorage(this.#ssKeySessionInfo, this.#authDynState); | ||
this.#pegaAuth = new PegaAuth(this.#ssKeyConfigInfo, this.#ssKeySessionInfo); | ||
} | ||
else { | ||
this.#authConfig.fnDynStateChangedCB = this.#doAuthDynStateChanged.bind(this); | ||
this.#pegaAuth = new PegaAuth(this.#authConfig, this.#authDynState); | ||
} | ||
this.initInProgress = false; | ||
resolve(this.#pegaAuth); | ||
}); | ||
}); | ||
return this.#promiseInitialize; | ||
} | ||
@@ -877,4 +882,2 @@ /** | ||
}; | ||
// eslint-disable-next-line no-console | ||
console.log('About to invoke PegaAuth authRedirectCallback'); | ||
this.authRedirectCallback(window.location.href, redirectDoneCB || cbDefault); | ||
@@ -881,0 +884,0 @@ }); |
@@ -10,3 +10,3 @@ /* eslint-disable no-console */ | ||
let SdkConfigAccess; | ||
let SdkConfigAccessCreateInProgress = false; | ||
let promiseSDKConfig = null; | ||
class ConfigAccess { | ||
@@ -135,35 +135,21 @@ constructor() { | ||
async function getSdkConfig() { | ||
return new Promise(resolve => { | ||
let idNextCheck = null; | ||
if (!SdkConfigAccess && !SdkConfigAccessCreateInProgress) { | ||
SdkConfigAccessCreateInProgress = true; | ||
createSdkConfigAccess().then(theConfigAccess => { | ||
// Key initialization of SdkConfigAccess | ||
SdkConfigAccess = theConfigAccess; | ||
SdkConfigAccessCreateInProgress = false; | ||
// console.log(`SdkConfigAccess: ${JSON.stringify(SdkConfigAccess)}`); | ||
// Create and dispatch the SdkConfigAccessReady event | ||
const event = new CustomEvent('SdkConfigAccessReady', {}); | ||
document.dispatchEvent(event); | ||
resolve(SdkConfigAccess.sdkConfig); | ||
}); | ||
} | ||
else { | ||
const fnCheckForConfig = () => { | ||
if (SdkConfigAccess) { | ||
if (idNextCheck) { | ||
clearInterval(idNextCheck); | ||
} | ||
resolve(SdkConfigAccess.sdkConfig); | ||
return; | ||
} | ||
idNextCheck = setInterval(fnCheckForConfig, 500); | ||
}; | ||
if (SdkConfigAccess) { | ||
resolve(SdkConfigAccess.sdkConfig); | ||
return; | ||
} | ||
idNextCheck = setInterval(fnCheckForConfig, 500); | ||
} | ||
if (promiseSDKConfig) { | ||
return promiseSDKConfig; | ||
} | ||
promiseSDKConfig = new Promise(resolve => { | ||
// let idNextCheck = null; | ||
// if (!SdkConfigAccess && !SdkConfigAccessCreateInProgress) { | ||
// SdkConfigAccessCreateInProgress = true; | ||
createSdkConfigAccess().then(theConfigAccess => { | ||
// Key initialization of SdkConfigAccess | ||
SdkConfigAccess = theConfigAccess; | ||
// SdkConfigAccessCreateInProgress = false; | ||
// console.log(`SdkConfigAccess: ${JSON.stringify(SdkConfigAccess)}`); | ||
// Create and dispatch the SdkConfigAccessReady event | ||
const event = new CustomEvent('SdkConfigAccessReady', {}); | ||
document.dispatchEvent(event); | ||
resolve(SdkConfigAccess.sdkConfig); | ||
}); | ||
}); | ||
return promiseSDKConfig; | ||
} | ||
@@ -170,0 +156,0 @@ getSdkConfig(); |
{ | ||
"name": "@pega/auth", | ||
"version": "0.2.16", | ||
"version": "0.2.17", | ||
"description": "Pega OAuth 2.0 Client Library (supports Infinity and Launchpad).", | ||
@@ -5,0 +5,0 @@ "repository": { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
205292
2251