@pega/auth 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/lib/auth-code-redirect.d.ts +2 -0
- package/lib/auth-code-redirect.d.ts.map +1 -0
- package/lib/auth-code-redirect.js +2 -0
- package/lib/auth-code-redirect.js.map +1 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +2 -0
- package/lib/index.js.map +1 -0
- package/lib/oauth-client/auth.d.ts +18 -0
- package/lib/oauth-client/auth.d.ts.map +1 -0
- package/lib/oauth-client/auth.js +760 -0
- package/lib/oauth-client/auth.js.map +1 -0
- package/lib/oauth-client/authCodeDone.d.ts +2 -0
- package/lib/oauth-client/authCodeDone.d.ts.map +1 -0
- package/lib/oauth-client/authCodeDone.js +84 -0
- package/lib/oauth-client/authCodeDone.js.map +1 -0
- package/lib/oauth-client/authDone.d.ts +2 -0
- package/lib/oauth-client/authDone.d.ts.map +1 -0
- package/lib/oauth-client/authDone.html +10 -0
- package/lib/oauth-client/authDone.js +87 -0
- package/lib/oauth-client/authDone.js.map +1 -0
- package/lib/oauth-client/lock-closed-solid.svg +5 -0
- package/lib/sdk-auth-manager/authManager.d.ts +21 -0
- package/lib/sdk-auth-manager/authManager.d.ts.map +1 -0
- package/lib/sdk-auth-manager/authManager.js +873 -0
- package/lib/sdk-auth-manager/authManager.js.map +1 -0
- package/lib/sdk-auth-manager/common-utils.d.ts +2 -0
- package/lib/sdk-auth-manager/common-utils.d.ts.map +1 -0
- package/lib/sdk-auth-manager/common-utils.js +4 -0
- package/lib/sdk-auth-manager/common-utils.js.map +1 -0
- package/lib/sdk-auth-manager/config_access.d.ts +3 -0
- package/lib/sdk-auth-manager/config_access.d.ts.map +1 -0
- package/lib/sdk-auth-manager/config_access.js +170 -0
- package/lib/sdk-auth-manager/config_access.js.map +1 -0
- package/lib/sdk-auth-manager.d.ts +3 -0
- package/lib/sdk-auth-manager.d.ts.map +1 -0
- package/lib/sdk-auth-manager.js +3 -0
- package/lib/sdk-auth-manager.js.map +1 -0
- package/package.json +54 -0
|
@@ -0,0 +1,873 @@
|
|
|
1
|
+
// This file wraps various calls related to logging in, logging out, etc.
|
|
2
|
+
// that use the auth.html/auth.js to do the work of logging in via OAuth 2.0.
|
|
3
|
+
// It utilizes a JS Class and private members to protect any sensitive tokens
|
|
4
|
+
// and token obfuscation routines
|
|
5
|
+
import { PegaAuth } from '../oauth-client/auth.js';
|
|
6
|
+
import { isEmptyObject } from './common-utils.js';
|
|
7
|
+
import { getSdkConfig, SdkConfigAccess } from './config_access.js';
|
|
8
|
+
// Meant to be a singleton...only one instance per page
|
|
9
|
+
class AuthManager {
|
|
10
|
+
#ssKeyPrefix = 'rs';
|
|
11
|
+
// will store the PegaAuth (OAuth 2.0 client library) instance
|
|
12
|
+
#pegaAuth = null;
|
|
13
|
+
#ssKeyConfigInfo = '';
|
|
14
|
+
#ssKeySessionInfo = '';
|
|
15
|
+
#ssKeyTokenInfo = '';
|
|
16
|
+
#ssKeyState = `${this.#ssKeyPrefix}State`;
|
|
17
|
+
#authConfig = {};
|
|
18
|
+
#authDynState = {};
|
|
19
|
+
#authHeader = null;
|
|
20
|
+
// state that should be persisted across loads
|
|
21
|
+
state = { usePopup: false, noInitialRedirect: false };
|
|
22
|
+
bC11NBootstrapInProgress = false;
|
|
23
|
+
bCustomAuth = false;
|
|
24
|
+
#tokenInfo;
|
|
25
|
+
#userInfo;
|
|
26
|
+
onLoadDone = false;
|
|
27
|
+
msReauthStart = null;
|
|
28
|
+
initInProgress = false;
|
|
29
|
+
isLoggedIn = false;
|
|
30
|
+
// Whether to pass a session storage key or structure to auth library
|
|
31
|
+
#usePASS = false;
|
|
32
|
+
#pageHideAdded = false;
|
|
33
|
+
#tokenStorage = 'temp';
|
|
34
|
+
#transform = true;
|
|
35
|
+
#foldSpot = 2;
|
|
36
|
+
constructor() {
|
|
37
|
+
// Auth Manager specific state is saved within session storage as important in redirect and popup window scenarios
|
|
38
|
+
this.#loadState();
|
|
39
|
+
}
|
|
40
|
+
#transformAndParse(ssKey, ssItem, bForce = false) {
|
|
41
|
+
let obj = {};
|
|
42
|
+
try {
|
|
43
|
+
obj = JSON.parse(this.#transformer(ssKey, ssItem, false, bForce));
|
|
44
|
+
}
|
|
45
|
+
catch (e) {
|
|
46
|
+
// fall thru and return empty object
|
|
47
|
+
}
|
|
48
|
+
return obj;
|
|
49
|
+
}
|
|
50
|
+
// helper routine to retrieve JSON object stored in a session storage key
|
|
51
|
+
// a 2nd optional arg can also retrieve an individual attribute
|
|
52
|
+
#getStorage(ssKey, sAttrib = null) {
|
|
53
|
+
const ssItem = ssKey ? window.sessionStorage.getItem(ssKey) : null;
|
|
54
|
+
let obj = {};
|
|
55
|
+
if (ssItem) {
|
|
56
|
+
try {
|
|
57
|
+
obj = JSON.parse(ssItem);
|
|
58
|
+
}
|
|
59
|
+
catch (e) {
|
|
60
|
+
obj = this.#transformAndParse(ssKey, ssItem, true);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return sAttrib ? obj[sAttrib] : obj;
|
|
64
|
+
}
|
|
65
|
+
// helper routine to set storage to the passed in JSON
|
|
66
|
+
#setStorage(ssKey, obj) {
|
|
67
|
+
// Set storage only if obj is not empty, else delete the storage
|
|
68
|
+
if (!obj || isEmptyObject(obj)) {
|
|
69
|
+
window.sessionStorage.removeItem(ssKey);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
// const bClear = (ssKey === this.#ssKeyState || ssKey === this.#ssKeySessionInfo);
|
|
73
|
+
const bClear = false;
|
|
74
|
+
const sValue = bClear
|
|
75
|
+
? JSON.stringify(obj)
|
|
76
|
+
: this.#transformer(ssKey, JSON.stringify(obj), true);
|
|
77
|
+
window.sessionStorage.setItem(ssKey, sValue);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
#calcFoldSpot(s) {
|
|
81
|
+
const nOffset = 1;
|
|
82
|
+
const sChar = s.length > nOffset ? s.charAt(nOffset) : '2';
|
|
83
|
+
const nSpot = parseInt(sChar, 10);
|
|
84
|
+
this.#foldSpot = Number.isNaN(nSpot) ? 2 : (nSpot % 4) + 2;
|
|
85
|
+
}
|
|
86
|
+
// helper function to encode storage
|
|
87
|
+
#transformer(ssKey, s, bIn, bForce = false) {
|
|
88
|
+
const bTransform = bForce || this.#transform;
|
|
89
|
+
const fnFold = (x) => {
|
|
90
|
+
const nLen = x.length;
|
|
91
|
+
const nExtra = nLen % this.#foldSpot;
|
|
92
|
+
const nOffset = Math.floor(nLen / this.#foldSpot) + nExtra;
|
|
93
|
+
const nRem = x.length - nOffset;
|
|
94
|
+
return x.substring(bIn ? nOffset : nRem) + x.substring(0, bIn ? nOffset : nRem);
|
|
95
|
+
};
|
|
96
|
+
const bTknInfo = ssKey === this.#ssKeyTokenInfo;
|
|
97
|
+
if (bTknInfo && !bIn && bTransform) {
|
|
98
|
+
s = window.atob(fnFold(s));
|
|
99
|
+
}
|
|
100
|
+
// eslint-disable-next-line no-nested-ternary
|
|
101
|
+
let result = bTransform ? (bIn ? window.btoa(s) : window.atob(s)) : s;
|
|
102
|
+
if (bTknInfo && bIn && bTransform) {
|
|
103
|
+
result = fnFold(window.btoa(result));
|
|
104
|
+
}
|
|
105
|
+
return result;
|
|
106
|
+
}
|
|
107
|
+
// Setter for authHeader (no getter)
|
|
108
|
+
set authHeader(value) {
|
|
109
|
+
this.#authHeader = value;
|
|
110
|
+
// setAuthorizationHeader method not available til 8.8 so do safety check
|
|
111
|
+
if (window.PCore?.getAuthUtils().setAuthorizationHeader) {
|
|
112
|
+
const authHdr = value === null ? '' : value;
|
|
113
|
+
window.PCore.getAuthUtils().setAuthorizationHeader(authHdr);
|
|
114
|
+
}
|
|
115
|
+
this.#updateLoginStatus();
|
|
116
|
+
}
|
|
117
|
+
// Setter/getter for usePopupForRestOfSession
|
|
118
|
+
set usePopupForRestOfSession(usePopup) {
|
|
119
|
+
this.state.usePopup = usePopup;
|
|
120
|
+
this.#setStorage(this.#ssKeyState, this.state);
|
|
121
|
+
}
|
|
122
|
+
get usePopupForRestOfSession() {
|
|
123
|
+
return this.state.usePopup;
|
|
124
|
+
}
|
|
125
|
+
// Setter/getter for noInitialRedirect
|
|
126
|
+
set noInitialRedirect(bNoInitialRedirect) {
|
|
127
|
+
if (bNoInitialRedirect) {
|
|
128
|
+
this.usePopupForRestOfSession = true;
|
|
129
|
+
}
|
|
130
|
+
this.state.noInitialRedirect = bNoInitialRedirect;
|
|
131
|
+
this.#setStorage(this.#ssKeyState, this.state);
|
|
132
|
+
}
|
|
133
|
+
get noInitialRedirect() {
|
|
134
|
+
return this.state.noInitialRedirect || false;
|
|
135
|
+
}
|
|
136
|
+
// Init/getter for loginStart
|
|
137
|
+
set loginStart(msValue) {
|
|
138
|
+
if (msValue) {
|
|
139
|
+
this.state.msLoginStart = msValue;
|
|
140
|
+
}
|
|
141
|
+
else if (this.state.msLoginStart) {
|
|
142
|
+
delete this.state.msLoginStart;
|
|
143
|
+
}
|
|
144
|
+
this.#setStorage(this.#ssKeyState, this.state);
|
|
145
|
+
}
|
|
146
|
+
get loginStart() {
|
|
147
|
+
return this.state.msLoginStart || 0;
|
|
148
|
+
}
|
|
149
|
+
// Init/getter for reauthStart
|
|
150
|
+
set reauthStart(msValue) {
|
|
151
|
+
if (msValue) {
|
|
152
|
+
this.msReauthStart = msValue;
|
|
153
|
+
}
|
|
154
|
+
else if (this.msReauthStart) {
|
|
155
|
+
delete this.msReauthStart;
|
|
156
|
+
}
|
|
157
|
+
this.#setStorage(this.#ssKeyState, this.state);
|
|
158
|
+
}
|
|
159
|
+
get reauthStart() {
|
|
160
|
+
return this.msReauthStart || 0;
|
|
161
|
+
}
|
|
162
|
+
// Setter for clientId
|
|
163
|
+
set keySuffix(s) {
|
|
164
|
+
this.state.sfx = s || undefined;
|
|
165
|
+
this.#setStorage(this.#ssKeyState, this.state);
|
|
166
|
+
if (s) {
|
|
167
|
+
// To make it a bit more obtuse reverse the string and use that as the actual suffix
|
|
168
|
+
const sSfx = s.split('').reverse().join('');
|
|
169
|
+
this.#ssKeyConfigInfo = `${this.#ssKeyPrefix}CI_${sSfx}`;
|
|
170
|
+
this.#ssKeySessionInfo = `${this.#ssKeyPrefix}SI_${sSfx}`;
|
|
171
|
+
this.#ssKeyTokenInfo = `${this.#ssKeyPrefix}TI_${sSfx}`;
|
|
172
|
+
this.#calcFoldSpot(sSfx);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
isLoginExpired() {
|
|
176
|
+
let bExpired = true;
|
|
177
|
+
if (this.loginStart) {
|
|
178
|
+
const currTime = Date.now();
|
|
179
|
+
bExpired = currTime - this.loginStart > 60000;
|
|
180
|
+
}
|
|
181
|
+
return bExpired;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Clean up any session storage allocated for the user session.
|
|
185
|
+
*/
|
|
186
|
+
clear(bFullReauth = false) {
|
|
187
|
+
if (!this.bCustomAuth) {
|
|
188
|
+
this.#authHeader = null;
|
|
189
|
+
}
|
|
190
|
+
if (!bFullReauth) {
|
|
191
|
+
if (this.#usePASS) {
|
|
192
|
+
sessionStorage.removeItem(this.#ssKeyConfigInfo);
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
this.#authConfig = {};
|
|
196
|
+
this.#authDynState = {};
|
|
197
|
+
}
|
|
198
|
+
sessionStorage.removeItem(this.#ssKeySessionInfo);
|
|
199
|
+
}
|
|
200
|
+
// Clear any established auth tokens
|
|
201
|
+
this.#tokenInfo = null;
|
|
202
|
+
sessionStorage.removeItem(this.#ssKeyTokenInfo);
|
|
203
|
+
this.loginStart = 0;
|
|
204
|
+
this.isLoggedIn = false;
|
|
205
|
+
// reset the initial redirect as well by using this setter
|
|
206
|
+
this.usePopupForRestOfSession = bFullReauth;
|
|
207
|
+
this.keySuffix = '';
|
|
208
|
+
}
|
|
209
|
+
#doPageHide() {
|
|
210
|
+
// Safari and particularly Safari on mobile devices doesn't seem to load this on first main redirect or
|
|
211
|
+
// reliably, so have moved to having PegaAuth manage writing all state props to session storage
|
|
212
|
+
this.#setStorage(this.#ssKeyState, this.state);
|
|
213
|
+
this.#setStorage(this.#ssKeySessionInfo, this.#authDynState);
|
|
214
|
+
// If tokenStorage was always, token would already be there
|
|
215
|
+
if (this.#tokenStorage === 'temp') {
|
|
216
|
+
this.#setStorage(this.#ssKeyTokenInfo, this.#tokenInfo);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
#loadState() {
|
|
220
|
+
// Note: State storage key doesn't have a client id associated with it
|
|
221
|
+
const oState = this.#getStorage(this.#ssKeyState);
|
|
222
|
+
if (oState) {
|
|
223
|
+
Object.assign(this.state, oState);
|
|
224
|
+
if (this.state.sfx) {
|
|
225
|
+
// Setter sets up the ssKey values as well
|
|
226
|
+
this.keySuffix = this.state.sfx;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
// This is only called from initialize after #ssKey values are setup
|
|
231
|
+
#doOnLoad() {
|
|
232
|
+
if (!this.onLoadDone) {
|
|
233
|
+
// This authConfig state doesn't collide with other calculated static state...so load it first
|
|
234
|
+
// Note: transform setting will have already been loaded into #authConfig at this point
|
|
235
|
+
this.#authDynState = this.#getStorage(this.#ssKeySessionInfo);
|
|
236
|
+
this.#tokenInfo = this.#getStorage(this.#ssKeyTokenInfo);
|
|
237
|
+
if (this.#tokenStorage !== 'always') {
|
|
238
|
+
sessionStorage.removeItem(this.#ssKeyTokenInfo);
|
|
239
|
+
sessionStorage.removeItem(this.#ssKeySessionInfo);
|
|
240
|
+
}
|
|
241
|
+
this.onLoadDone = true;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
// Callback when auth dynamic state has changed. Decide whether to persisting it based on
|
|
245
|
+
// config settings
|
|
246
|
+
#doAuthDynStateChanged() {
|
|
247
|
+
// If tokenStorage is setup for always then always persist the auth dynamic state as well
|
|
248
|
+
if (this.#tokenStorage === 'always') {
|
|
249
|
+
this.#setStorage(this.#ssKeySessionInfo, this.#authDynState);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Initialize OAuth config structure members and create authMgr instance (if necessary)
|
|
254
|
+
* bNew - governs whether to create new sessionStorage or load existing one
|
|
255
|
+
*/
|
|
256
|
+
async #initialize(bNew = false) {
|
|
257
|
+
return new Promise(resolve => {
|
|
258
|
+
if (!this.initInProgress && (bNew || isEmptyObject(this.#authConfig) || !this.#pegaAuth)) {
|
|
259
|
+
this.initInProgress = true;
|
|
260
|
+
getSdkConfig().then(sdkConfig => {
|
|
261
|
+
const sdkConfigAuth = sdkConfig.authConfig;
|
|
262
|
+
const sdkConfigServer = sdkConfig.serverConfig;
|
|
263
|
+
let pegaUrl = sdkConfigServer.infinityRestServerUrl;
|
|
264
|
+
const bNoInitialRedirect = this.noInitialRedirect;
|
|
265
|
+
// Construct default OAuth endpoints (if not explicitly specified)
|
|
266
|
+
if (pegaUrl) {
|
|
267
|
+
// Cope with trailing slash being present
|
|
268
|
+
if (!pegaUrl.endsWith('/')) {
|
|
269
|
+
pegaUrl += '/';
|
|
270
|
+
}
|
|
271
|
+
if (!sdkConfigAuth.authorize) {
|
|
272
|
+
sdkConfigAuth.authorize = `${pegaUrl}PRRestService/oauth2/v1/authorize`;
|
|
273
|
+
}
|
|
274
|
+
if (!sdkConfigAuth.token) {
|
|
275
|
+
sdkConfigAuth.token = `${pegaUrl}PRRestService/oauth2/v1/token`;
|
|
276
|
+
}
|
|
277
|
+
if (!sdkConfigAuth.revoke) {
|
|
278
|
+
sdkConfigAuth.revoke = `${pegaUrl}PRRestService/oauth2/v1/revoke`;
|
|
279
|
+
}
|
|
280
|
+
if (!sdkConfigAuth.redirectUri) {
|
|
281
|
+
sdkConfigAuth.redirectUri = `${window.location.origin}${window.location.pathname}`;
|
|
282
|
+
}
|
|
283
|
+
if (!sdkConfigAuth.userinfo) {
|
|
284
|
+
const appAliasSeg = sdkConfigServer.appAlias
|
|
285
|
+
? `app/${sdkConfigServer.appAlias}/`
|
|
286
|
+
: '';
|
|
287
|
+
sdkConfigAuth.userinfo = `${pegaUrl}${appAliasSeg}api/oauthclients/v1/userinfo/JSON`;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
// Auth service alias
|
|
291
|
+
if (!sdkConfigAuth.authService) {
|
|
292
|
+
sdkConfigAuth.authService = 'pega';
|
|
293
|
+
}
|
|
294
|
+
// mashupAuthService provides way to have a different auth service for embedded
|
|
295
|
+
if (!sdkConfigAuth.mashupAuthService) {
|
|
296
|
+
sdkConfigAuth.mashupAuthService = sdkConfigAuth.authService;
|
|
297
|
+
}
|
|
298
|
+
// Construct path to auth.html (used for case when not doing a main window redirect)
|
|
299
|
+
let sNoMainRedirectUri = sdkConfigAuth.redirectUri;
|
|
300
|
+
const nLastPathSep = sNoMainRedirectUri.lastIndexOf('/');
|
|
301
|
+
sNoMainRedirectUri =
|
|
302
|
+
nLastPathSep !== -1
|
|
303
|
+
? `${sNoMainRedirectUri.substring(0, nLastPathSep + 1)}auth.html`
|
|
304
|
+
: `${sNoMainRedirectUri}/auth.html`;
|
|
305
|
+
const portalGrantType = sdkConfigAuth.portalGrantType || 'authCode';
|
|
306
|
+
const mashupGrantType = sdkConfigAuth.mashupGrantType || 'authCode';
|
|
307
|
+
const pegaAuthConfig = {
|
|
308
|
+
clientId: bNoInitialRedirect
|
|
309
|
+
? sdkConfigAuth.mashupClientId
|
|
310
|
+
: sdkConfigAuth.portalClientId,
|
|
311
|
+
grantType: bNoInitialRedirect ? mashupGrantType : portalGrantType,
|
|
312
|
+
tokenUri: sdkConfigAuth.token,
|
|
313
|
+
revokeUri: sdkConfigAuth.revoke,
|
|
314
|
+
userinfoUri: sdkConfigAuth.userinfo,
|
|
315
|
+
authService: bNoInitialRedirect
|
|
316
|
+
? sdkConfigAuth.mashupAuthService
|
|
317
|
+
: sdkConfigAuth.authService,
|
|
318
|
+
appAlias: sdkConfigServer.appAlias || '',
|
|
319
|
+
useLocking: true
|
|
320
|
+
};
|
|
321
|
+
// Invoke keySuffix setter
|
|
322
|
+
// Was using pegaAuthConfig.clientId as key but more secure to just use a random string as getting
|
|
323
|
+
// both a clientId and the refresh token could yield a new access token.
|
|
324
|
+
// Suffix is so we might in future move to an array of suffixes based on the appName, so might store
|
|
325
|
+
// both portal and embedded tokens/session info at same time
|
|
326
|
+
if (!this.state?.sfx) {
|
|
327
|
+
// Just using a random number to make the suffix unique on each session
|
|
328
|
+
this.keySuffix = `${Math.ceil(Math.random() * 100000000)}`;
|
|
329
|
+
}
|
|
330
|
+
this.#authConfig.transform =
|
|
331
|
+
sdkConfigAuth.transform !== undefined ? sdkConfigAuth.transform : this.#transform;
|
|
332
|
+
// Using property in class as authConfig may be empty at times
|
|
333
|
+
this.#transform = this.#authConfig.transform;
|
|
334
|
+
if (sdkConfigAuth.tokenStorage !== undefined) {
|
|
335
|
+
this.#tokenStorage = sdkConfigAuth.tokenStorage;
|
|
336
|
+
}
|
|
337
|
+
// Get latest state once client ids, transform and tokenStorage have been established
|
|
338
|
+
this.#doOnLoad();
|
|
339
|
+
// If no clientId is specified assume not OAuth but custom auth
|
|
340
|
+
if (!pegaAuthConfig.clientId) {
|
|
341
|
+
this.bCustomAuth = true;
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
if (pegaAuthConfig.grantType === 'authCode') {
|
|
345
|
+
const authCodeProps = {
|
|
346
|
+
authorizeUri: sdkConfigAuth.authorize,
|
|
347
|
+
// If we have already specified a redirect on the authorize redirect, we need to continue to use that
|
|
348
|
+
// on token endpoint
|
|
349
|
+
redirectUri: bNoInitialRedirect || this.usePopupForRestOfSession
|
|
350
|
+
? sNoMainRedirectUri
|
|
351
|
+
: sdkConfigAuth.redirectUri
|
|
352
|
+
};
|
|
353
|
+
if ('silentTimeout' in sdkConfigAuth) {
|
|
354
|
+
authCodeProps.silentTimeout = sdkConfigAuth.silentTimeout;
|
|
355
|
+
}
|
|
356
|
+
if (bNoInitialRedirect &&
|
|
357
|
+
pegaAuthConfig.authService === 'pega' &&
|
|
358
|
+
sdkConfigAuth.mashupUserIdentifier &&
|
|
359
|
+
sdkConfigAuth.mashupPassword) {
|
|
360
|
+
authCodeProps.userIdentifier = sdkConfigAuth.mashupUserIdentifier;
|
|
361
|
+
authCodeProps.password = sdkConfigAuth.mashupPassword;
|
|
362
|
+
}
|
|
363
|
+
if ('iframeLoginUI' in sdkConfigAuth) {
|
|
364
|
+
authCodeProps.iframeLoginUI =
|
|
365
|
+
sdkConfigAuth.iframeLoginUI.toString().toLowerCase() === 'true';
|
|
366
|
+
}
|
|
367
|
+
Object.assign(pegaAuthConfig, authCodeProps);
|
|
368
|
+
}
|
|
369
|
+
Object.assign(this.#authConfig, pegaAuthConfig);
|
|
370
|
+
// Add an on page hide handler to write out key properties that we want to survive a
|
|
371
|
+
// browser reload
|
|
372
|
+
if (!this.#pageHideAdded && (!this.#usePASS || this.#tokenStorage !== 'always')) {
|
|
373
|
+
window.addEventListener('pagehide', this.#doPageHide.bind(this));
|
|
374
|
+
this.#pageHideAdded = true;
|
|
375
|
+
}
|
|
376
|
+
// Initialize PegaAuth OAuth 2.0 client library
|
|
377
|
+
if (this.#usePASS) {
|
|
378
|
+
this.#setStorage(this.#ssKeyConfigInfo, this.#authConfig);
|
|
379
|
+
this.#setStorage(this.#ssKeySessionInfo, this.#authDynState);
|
|
380
|
+
this.#pegaAuth = new PegaAuth(this.#ssKeyConfigInfo, this.#ssKeySessionInfo);
|
|
381
|
+
}
|
|
382
|
+
else {
|
|
383
|
+
this.#authConfig.fnDynStateChangedCB = this.#doAuthDynStateChanged.bind(this);
|
|
384
|
+
this.#pegaAuth = new PegaAuth(this.#authConfig, this.#authDynState);
|
|
385
|
+
}
|
|
386
|
+
this.initInProgress = false;
|
|
387
|
+
resolve(this.#pegaAuth);
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
else {
|
|
391
|
+
let idNextCheck;
|
|
392
|
+
const fnCheckForAuthMgr = () => {
|
|
393
|
+
if (!this.initInProgress) {
|
|
394
|
+
if (idNextCheck) {
|
|
395
|
+
clearInterval(idNextCheck);
|
|
396
|
+
}
|
|
397
|
+
resolve(this.#pegaAuth);
|
|
398
|
+
}
|
|
399
|
+
};
|
|
400
|
+
fnCheckForAuthMgr();
|
|
401
|
+
idNextCheck = setInterval(fnCheckForAuthMgr, 100);
|
|
402
|
+
}
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Initiate the process to get the Constellation bootstrap shell loaded and initialized
|
|
407
|
+
* @param {Object} authConfig
|
|
408
|
+
* @param {Object} tokenInfo
|
|
409
|
+
* @param {Function} authTokenUpdated - callback invoked when Constellation JS Engine silently updates
|
|
410
|
+
* an expired access_token
|
|
411
|
+
* @param {Function} fnReauth - callback invoked when a full or custom reauth is needed
|
|
412
|
+
*/
|
|
413
|
+
#constellationInit(authConfig, tokenInfo, authTokenUpdated, fnReauth) {
|
|
414
|
+
const constellationBootConfig = {};
|
|
415
|
+
const sdkConfigServer = SdkConfigAccess.getSdkConfigServer();
|
|
416
|
+
// Set up constellationConfig with data that bootstrapWithAuthHeader expects
|
|
417
|
+
constellationBootConfig.customRendering = true;
|
|
418
|
+
constellationBootConfig.restServerUrl = sdkConfigServer.infinityRestServerUrl;
|
|
419
|
+
// NOTE: Needs a trailing slash! So add one if not provided
|
|
420
|
+
if (!sdkConfigServer.sdkContentServerUrl.endsWith('/')) {
|
|
421
|
+
sdkConfigServer.sdkContentServerUrl = `${sdkConfigServer.sdkContentServerUrl}/`;
|
|
422
|
+
}
|
|
423
|
+
constellationBootConfig.staticContentServerUrl = `${sdkConfigServer.sdkContentServerUrl}constellation/`;
|
|
424
|
+
if (!constellationBootConfig.staticContentServerUrl.endsWith('/')) {
|
|
425
|
+
constellationBootConfig.staticContentServerUrl = `${constellationBootConfig.staticContentServerUrl}/`;
|
|
426
|
+
}
|
|
427
|
+
// If appAlias specified, use it
|
|
428
|
+
if (sdkConfigServer.appAlias) {
|
|
429
|
+
constellationBootConfig.appAlias = sdkConfigServer.appAlias;
|
|
430
|
+
}
|
|
431
|
+
if (tokenInfo) {
|
|
432
|
+
// Pass in auth info to Constellation
|
|
433
|
+
constellationBootConfig.authInfo = {
|
|
434
|
+
authType: 'OAuth2.0',
|
|
435
|
+
tokenInfo,
|
|
436
|
+
// Set whether we want constellation to try to do a full re-Auth or not ()
|
|
437
|
+
// true doesn't seem to be working in SDK scenario so always passing false for now
|
|
438
|
+
popupReauth: false /* !this.noInitialRedirect */,
|
|
439
|
+
client_id: authConfig.clientId,
|
|
440
|
+
authentication_service: authConfig.authService,
|
|
441
|
+
redirect_uri: authConfig.redirectUri,
|
|
442
|
+
endPoints: {
|
|
443
|
+
authorize: authConfig.authorizeUri,
|
|
444
|
+
token: authConfig.tokenUri,
|
|
445
|
+
revoke: authConfig.revokeUri
|
|
446
|
+
},
|
|
447
|
+
// TODO: setup callback so we can update own storage
|
|
448
|
+
onTokenRetrieval: this.#authTokenUpdated.bind(this)
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
else {
|
|
452
|
+
constellationBootConfig.authorizationHeader = this.#authHeader;
|
|
453
|
+
}
|
|
454
|
+
// Turn off dynamic load components (should be able to do it here instead of after load?)
|
|
455
|
+
constellationBootConfig.dynamicLoadComponents = false;
|
|
456
|
+
if (this.bC11NBootstrapInProgress) {
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
this.bC11NBootstrapInProgress = true;
|
|
460
|
+
// Note that staticContentServerUrl already ends with a slash (see above), so no slash added.
|
|
461
|
+
// In order to have this import succeed and to have it done with the webpackIgnore magic comment tag.
|
|
462
|
+
// See: https://webpack.js.org/api/module-methods/
|
|
463
|
+
import(
|
|
464
|
+
/* webpackIgnore: true */ `${constellationBootConfig.staticContentServerUrl}bootstrap-shell.js`).then(bootstrapShell => {
|
|
465
|
+
// NOTE: once this callback is done, we lose the ability to access loadMashup.
|
|
466
|
+
// So, create a reference to it
|
|
467
|
+
window.myLoadMashup = bootstrapShell.loadMashup;
|
|
468
|
+
window.myLoadPortal = bootstrapShell.loadPortal;
|
|
469
|
+
window.myLoadDefaultPortal = bootstrapShell.loadDefaultPortal;
|
|
470
|
+
bootstrapShell
|
|
471
|
+
.bootstrapWithAuthHeader(constellationBootConfig, 'pega-root')
|
|
472
|
+
.then(() => {
|
|
473
|
+
// eslint-disable-next-line no-console
|
|
474
|
+
console.log('ConstellationJS bootstrap successful!');
|
|
475
|
+
this.bC11NBootstrapInProgress = false;
|
|
476
|
+
// Setup listener for the reauth event
|
|
477
|
+
if (tokenInfo) {
|
|
478
|
+
PCore.getPubSubUtils().subscribe(PCore.getConstants().PUB_SUB_EVENTS.EVENT_FULL_REAUTH, fnReauth, 'authFullReauth');
|
|
479
|
+
}
|
|
480
|
+
else {
|
|
481
|
+
// customReauth event introduced with 8.8
|
|
482
|
+
const sEvent = PCore.getConstants().PUB_SUB_EVENTS.EVENT_CUSTOM_REAUTH;
|
|
483
|
+
if (sEvent) {
|
|
484
|
+
PCore.getPubSubUtils().subscribe(sEvent, fnReauth, 'doReauth');
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
// Fire SdkConstellationReady event so bridge and app route can do expected post PCore initializations
|
|
488
|
+
const event = new CustomEvent('SdkConstellationReady', {});
|
|
489
|
+
document.dispatchEvent(event);
|
|
490
|
+
})
|
|
491
|
+
.catch(e => {
|
|
492
|
+
// Assume error caught is because token is not valid and attempt a full reauth
|
|
493
|
+
// eslint-disable-next-line no-console
|
|
494
|
+
console.error(`ConstellationJS bootstrap failed. ${e}`);
|
|
495
|
+
this.bC11NBootstrapInProgress = false;
|
|
496
|
+
fnReauth();
|
|
497
|
+
});
|
|
498
|
+
});
|
|
499
|
+
/* Ends here */
|
|
500
|
+
}
|
|
501
|
+
#customConstellationInit(fnReauth) {
|
|
502
|
+
this.#constellationInit(null, null, null, fnReauth);
|
|
503
|
+
}
|
|
504
|
+
#fireTokenAvailable(token, bLoadC11N = true) {
|
|
505
|
+
if (!token) {
|
|
506
|
+
// This is used on page reload to load the token from sessionStorage and carry on
|
|
507
|
+
token = this.#tokenInfo;
|
|
508
|
+
if (!token) {
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
this.#tokenInfo = token;
|
|
513
|
+
if (this.#tokenStorage === 'always') {
|
|
514
|
+
this.#setStorage(this.#ssKeyTokenInfo, this.#tokenInfo);
|
|
515
|
+
}
|
|
516
|
+
this.#updateLoginStatus();
|
|
517
|
+
// this.isLoggedIn is getting updated in updateLoginStatus
|
|
518
|
+
this.isLoggedIn = true;
|
|
519
|
+
this.loginStart = 0;
|
|
520
|
+
this.usePopupForRestOfSession = true;
|
|
521
|
+
if (!window.PCore && bLoadC11N) {
|
|
522
|
+
this.#constellationInit(this.#authConfig, token, this.#authTokenUpdated.bind(this), this.#authFullReauth.bind(this));
|
|
523
|
+
}
|
|
524
|
+
/*
|
|
525
|
+
// Create and dispatch the SdkLoggedIn event to trigger constellationInit
|
|
526
|
+
const event = new CustomEvent('SdkLoggedIn', { detail: { authConfig, tokenInfo: token } });
|
|
527
|
+
document.dispatchEvent(event);
|
|
528
|
+
*/
|
|
529
|
+
}
|
|
530
|
+
#processTokenOnLogin(token, bLoadC11N = true) {
|
|
531
|
+
this.#tokenInfo = token;
|
|
532
|
+
if (this.#tokenStorage === 'always') {
|
|
533
|
+
this.#setStorage(this.#ssKeyTokenInfo, this.#tokenInfo);
|
|
534
|
+
}
|
|
535
|
+
if (window.PCore) {
|
|
536
|
+
PCore.getAuthUtils().setTokens(token);
|
|
537
|
+
}
|
|
538
|
+
else {
|
|
539
|
+
this.#fireTokenAvailable(token, bLoadC11N);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
updateRedirectUri(sRedirectUri) {
|
|
543
|
+
this.#authConfig.redirectUri = sRedirectUri;
|
|
544
|
+
}
|
|
545
|
+
/**
|
|
546
|
+
* Get available portals which supports SDK
|
|
547
|
+
* @returns list of available portals (portals other than excludingPortals list)
|
|
548
|
+
*/
|
|
549
|
+
async getAvailablePortals() {
|
|
550
|
+
return getSdkConfig().then(sdkConfig => {
|
|
551
|
+
const serverConfig = sdkConfig.serverConfig;
|
|
552
|
+
const userAccessGroup = PCore.getEnvironmentInfo().getAccessGroup();
|
|
553
|
+
const dataPageName = 'D_OperatorAccessGroups';
|
|
554
|
+
const serverUrl = serverConfig.infinityRestServerUrl;
|
|
555
|
+
const appAlias = serverConfig.appAlias;
|
|
556
|
+
const appAliasPath = appAlias ? `/app/${appAlias}` : '';
|
|
557
|
+
const arExcludedPortals = serverConfig.excludePortals;
|
|
558
|
+
const headers = {
|
|
559
|
+
Authorization: this.#authHeader === null ? '' : this.#authHeader,
|
|
560
|
+
'Content-Type': 'application/json'
|
|
561
|
+
};
|
|
562
|
+
// Using v1 API here as v2 data_views is not able to access same data page currently. Should move to avoid having this logic to find
|
|
563
|
+
// a default portal or constellation portal and rather have Constellation JS Engine API just load the default portal
|
|
564
|
+
return fetch(`${serverUrl}${appAliasPath}/api/v1/data/${dataPageName}`, {
|
|
565
|
+
method: 'GET',
|
|
566
|
+
headers
|
|
567
|
+
})
|
|
568
|
+
.then(response => {
|
|
569
|
+
if (response.ok && response.status === 200) {
|
|
570
|
+
return response.json();
|
|
571
|
+
}
|
|
572
|
+
if (response.status === 401) {
|
|
573
|
+
// Might be either a real token expiration or revoke, but more likely that the "api" service package is misconfigured
|
|
574
|
+
throw new Error(`Attempt to access ${dataPageName} failed. The "api" service package is likely not configured to use "OAuth 2.0"`);
|
|
575
|
+
}
|
|
576
|
+
throw new Error(`HTTP Error: ${response.status}`);
|
|
577
|
+
})
|
|
578
|
+
.then(async (agData) => {
|
|
579
|
+
const arAccessGroups = agData.pxResults;
|
|
580
|
+
const availablePortals = [];
|
|
581
|
+
for (const ag of arAccessGroups) {
|
|
582
|
+
if (ag.pyAccessGroup === userAccessGroup) {
|
|
583
|
+
// eslint-disable-next-line no-console
|
|
584
|
+
console.error(`Default portal for current operator (${ag.pyPortal}) is not compatible with SDK.\nConsider using a different operator, adjusting the default portal for this operator, or using "appPortal" setting within sdk-config.json to specify a specific portal to load.`);
|
|
585
|
+
let portal = null;
|
|
586
|
+
for (portal of ag.pyUserPortals) {
|
|
587
|
+
if (!arExcludedPortals.includes(portal.pyPortalLayout)) {
|
|
588
|
+
availablePortals.push(portal.pyPortalLayout);
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
break;
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
// Found operator's current access group. Use its portal
|
|
595
|
+
// eslint-disable-next-line no-console
|
|
596
|
+
console.log(`Available portals: ${availablePortals}`);
|
|
597
|
+
return availablePortals;
|
|
598
|
+
})
|
|
599
|
+
.catch(e => {
|
|
600
|
+
// eslint-disable-next-line no-console
|
|
601
|
+
console.error(e.message);
|
|
602
|
+
// check specific error if 401, and wiped out if so stored token is stale. Fetch new tokens.
|
|
603
|
+
});
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
#updateLoginStatus() {
|
|
607
|
+
if (!this.#authHeader && this.#tokenInfo?.access_token) {
|
|
608
|
+
// Use setter to set this securely
|
|
609
|
+
this.authHeader = `${this.#tokenInfo.token_type} ${this.#tokenInfo.access_token}`;
|
|
610
|
+
}
|
|
611
|
+
this.isLoggedIn = !!(this.#authHeader && this.#authHeader.length > 0);
|
|
612
|
+
}
|
|
613
|
+
// Initiate a full OAuth re-authorization (any refresh token has also expired).
|
|
614
|
+
#authFullReauth() {
|
|
615
|
+
const bHandleHere = true; // Other alternative is to raise an event and have someone else handle it
|
|
616
|
+
if (this.reauthStart) {
|
|
617
|
+
const reauthIgnoreInterval = 300000; // 5 minutes
|
|
618
|
+
const currTime = Date.now();
|
|
619
|
+
const bReauthInProgress = currTime - this.reauthStart <= reauthIgnoreInterval;
|
|
620
|
+
if (bReauthInProgress) {
|
|
621
|
+
return;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
if (bHandleHere) {
|
|
625
|
+
// Don't want to do a full clear of authMgr as will loose state props (like sessionIndex). Rather just clear the tokens
|
|
626
|
+
this.clear(true);
|
|
627
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
|
628
|
+
login(true);
|
|
629
|
+
}
|
|
630
|
+
else {
|
|
631
|
+
// Fire the SdkFullReauth event to indicate a new token is needed (PCore.getAuthUtils.setTokens method
|
|
632
|
+
// should be used to communicate the new token to Constellation JS Engine.
|
|
633
|
+
const event = new CustomEvent('SdkFullReauth', {
|
|
634
|
+
detail: this.#processTokenOnLogin.bind(this)
|
|
635
|
+
});
|
|
636
|
+
document.dispatchEvent(event);
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
// Passive update where just session storage is updated so can be used on a window refresh
|
|
640
|
+
#authTokenUpdated(tokenInfo) {
|
|
641
|
+
this.#tokenInfo = tokenInfo;
|
|
642
|
+
}
|
|
643
|
+
// TODO: Cope with 401 and refresh token if possible (or just hope that it succeeds during login)
|
|
644
|
+
/**
|
|
645
|
+
* Retrieve UserInfo for current authentication service
|
|
646
|
+
*/
|
|
647
|
+
getUserInfo() {
|
|
648
|
+
if (this.#userInfo) {
|
|
649
|
+
return this.#userInfo;
|
|
650
|
+
}
|
|
651
|
+
return this.#initialize(false).then(aMgr => {
|
|
652
|
+
return aMgr.getUserinfo(this.#tokenInfo.access_token).then(data => {
|
|
653
|
+
this.#userInfo = data;
|
|
654
|
+
return this.#userInfo;
|
|
655
|
+
});
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
login(bFullReauth = false) {
|
|
659
|
+
if (this.bCustomAuth)
|
|
660
|
+
return;
|
|
661
|
+
// Needed so a redirect to login screen and back will know we are still in process of logging in
|
|
662
|
+
this.loginStart = Date.now();
|
|
663
|
+
this.#initialize(!bFullReauth).then(aMgr => {
|
|
664
|
+
const bMainRedirect = !this.noInitialRedirect;
|
|
665
|
+
const sdkConfigAuth = SdkConfigAccess.getSdkConfigAuth();
|
|
666
|
+
let sRedirectUri = sdkConfigAuth.redirectUri;
|
|
667
|
+
// If initial main redirect is OK, redirect to main page, otherwise will authorize in a popup window
|
|
668
|
+
if (bMainRedirect && !bFullReauth) {
|
|
669
|
+
// update redirect uri to be the root
|
|
670
|
+
this.updateRedirectUri(sRedirectUri);
|
|
671
|
+
aMgr.loginRedirect();
|
|
672
|
+
// Don't have token til after the redirect
|
|
673
|
+
return Promise.resolve(undefined);
|
|
674
|
+
}
|
|
675
|
+
// Construct path to redirect uri
|
|
676
|
+
const nLastPathSep = sRedirectUri.lastIndexOf('/');
|
|
677
|
+
sRedirectUri =
|
|
678
|
+
nLastPathSep !== -1
|
|
679
|
+
? `${sRedirectUri.substring(0, nLastPathSep + 1)}auth.html`
|
|
680
|
+
: `${sRedirectUri}/auth.html`;
|
|
681
|
+
// Set redirectUri to static auth.html
|
|
682
|
+
this.updateRedirectUri(sRedirectUri);
|
|
683
|
+
return new Promise((resolve, reject) => {
|
|
684
|
+
aMgr
|
|
685
|
+
.login()
|
|
686
|
+
.then(token => {
|
|
687
|
+
this.#processTokenOnLogin(token);
|
|
688
|
+
// this.getUserInfo();
|
|
689
|
+
resolve(token.access_token);
|
|
690
|
+
})
|
|
691
|
+
.catch(e => {
|
|
692
|
+
// Use setter to update state
|
|
693
|
+
this.loginStart = 0;
|
|
694
|
+
// eslint-disable-next-line no-console
|
|
695
|
+
console.log(e);
|
|
696
|
+
reject(e);
|
|
697
|
+
});
|
|
698
|
+
});
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
authRedirectCallback(href, fnLoggedInCB = null) {
|
|
702
|
+
// Get code from href and swap for token
|
|
703
|
+
const aHrefParts = href.split('?');
|
|
704
|
+
const urlParams = new URLSearchParams(aHrefParts.length > 1 ? `?${aHrefParts[1]}` : '');
|
|
705
|
+
const code = urlParams.get('code');
|
|
706
|
+
const state = urlParams.get('state');
|
|
707
|
+
// If state should also match before accepting code
|
|
708
|
+
if (code) {
|
|
709
|
+
this.#initialize(false).then(aMgr => {
|
|
710
|
+
if (aMgr.checkStateMatch(state)) {
|
|
711
|
+
aMgr.getToken(code).then(token => {
|
|
712
|
+
if (token && token.access_token) {
|
|
713
|
+
this.#processTokenOnLogin(token, false);
|
|
714
|
+
// this.getUserInfo();
|
|
715
|
+
if (fnLoggedInCB) {
|
|
716
|
+
fnLoggedInCB(token.access_token);
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
});
|
|
720
|
+
}
|
|
721
|
+
});
|
|
722
|
+
}
|
|
723
|
+
else {
|
|
724
|
+
const error = urlParams.get('error');
|
|
725
|
+
const errorDesc = urlParams.get('errorDesc');
|
|
726
|
+
fnLoggedInCB(null, error, errorDesc);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
loginIfNecessary(loginProps) {
|
|
730
|
+
const { appName, deferLogin, redirectDoneCB } = loginProps;
|
|
731
|
+
const noMainRedirect = !loginProps.mainRedirect;
|
|
732
|
+
// We need to load state before making any decisions
|
|
733
|
+
this.#loadState();
|
|
734
|
+
// If no initial redirect status of page changed...clear AuthMgr
|
|
735
|
+
const currNoMainRedirect = this.noInitialRedirect;
|
|
736
|
+
if (appName !== this.state.appName || noMainRedirect !== currNoMainRedirect) {
|
|
737
|
+
this.clear(false);
|
|
738
|
+
this.state.appName = appName;
|
|
739
|
+
this.#setStorage(this.#ssKeyState, this.state);
|
|
740
|
+
}
|
|
741
|
+
this.noInitialRedirect = noMainRedirect;
|
|
742
|
+
// If custom auth no need to do any OAuth logic
|
|
743
|
+
if (this.bCustomAuth) {
|
|
744
|
+
this.#updateLoginStatus();
|
|
745
|
+
if (!window.PCore) {
|
|
746
|
+
this.#customConstellationInit(() => {
|
|
747
|
+
// Fire the SdkCustomReauth event to indicate a new authHeader is needed. Event listener should invoke sdkSetAuthHeader
|
|
748
|
+
// to communicate the new token to sdk (and Constellation JS Engine)
|
|
749
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
|
750
|
+
const event = new CustomEvent('SdkCustomReauth', { detail: sdkSetAuthHeader });
|
|
751
|
+
document.dispatchEvent(event);
|
|
752
|
+
});
|
|
753
|
+
}
|
|
754
|
+
return;
|
|
755
|
+
}
|
|
756
|
+
if (window.location.href.includes('?code')) {
|
|
757
|
+
// initialize authMgr (now initialize in constructor?)
|
|
758
|
+
return this.#initialize(false).then(() => {
|
|
759
|
+
const cbDefault = () => {
|
|
760
|
+
window.location.href = window.location.pathname;
|
|
761
|
+
};
|
|
762
|
+
// eslint-disable-next-line no-console
|
|
763
|
+
console.log('About to invoke PegaAuth authRedirectCallback');
|
|
764
|
+
this.authRedirectCallback(window.location.href, redirectDoneCB || cbDefault);
|
|
765
|
+
// });
|
|
766
|
+
});
|
|
767
|
+
}
|
|
768
|
+
if (!deferLogin && (!this.loginStart || this.isLoginExpired())) {
|
|
769
|
+
return this.#initialize(false).then(() => {
|
|
770
|
+
this.#updateLoginStatus();
|
|
771
|
+
if (this.isLoggedIn) {
|
|
772
|
+
this.#fireTokenAvailable(this.#tokenInfo);
|
|
773
|
+
// this.getUserInfo();
|
|
774
|
+
}
|
|
775
|
+
else {
|
|
776
|
+
return this.login();
|
|
777
|
+
}
|
|
778
|
+
// });
|
|
779
|
+
});
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
logout() {
|
|
783
|
+
sessionStorage.removeItem('sdk_portalName');
|
|
784
|
+
return new Promise(resolve => {
|
|
785
|
+
const fnClearAndResolve = () => {
|
|
786
|
+
this.clear();
|
|
787
|
+
const event = new Event('SdkLoggedOut');
|
|
788
|
+
document.dispatchEvent(event);
|
|
789
|
+
resolve(null);
|
|
790
|
+
};
|
|
791
|
+
if (this.bCustomAuth) {
|
|
792
|
+
fnClearAndResolve();
|
|
793
|
+
return;
|
|
794
|
+
}
|
|
795
|
+
if (this.#tokenInfo && this.#tokenInfo.access_token) {
|
|
796
|
+
if (window.PCore) {
|
|
797
|
+
window.PCore.getAuthUtils()
|
|
798
|
+
.revokeTokens()
|
|
799
|
+
.then(() => {
|
|
800
|
+
fnClearAndResolve();
|
|
801
|
+
})
|
|
802
|
+
.catch(err => {
|
|
803
|
+
// eslint-disable-next-line no-console
|
|
804
|
+
console.log('Error:', err?.message);
|
|
805
|
+
});
|
|
806
|
+
}
|
|
807
|
+
else {
|
|
808
|
+
this.#initialize(false).then(aMgr => {
|
|
809
|
+
aMgr
|
|
810
|
+
.revokeTokens(this.#tokenInfo.access_token, this.#tokenInfo.refresh_token)
|
|
811
|
+
.then(() => {
|
|
812
|
+
// Go to finally
|
|
813
|
+
})
|
|
814
|
+
.finally(() => {
|
|
815
|
+
fnClearAndResolve();
|
|
816
|
+
});
|
|
817
|
+
});
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
else {
|
|
821
|
+
fnClearAndResolve();
|
|
822
|
+
}
|
|
823
|
+
});
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
const gAuthMgr = new AuthManager();
|
|
827
|
+
// TODO: Cope with 401 and refresh token if possible (or just hope that it succeeds during login)
|
|
828
|
+
/**
|
|
829
|
+
* Retrieve UserInfo for current authentication service
|
|
830
|
+
*/
|
|
831
|
+
export const getUserInfo = () => {
|
|
832
|
+
return gAuthMgr.getUserInfo();
|
|
833
|
+
};
|
|
834
|
+
export const login = (bFullReauth = false) => {
|
|
835
|
+
return gAuthMgr.login(bFullReauth);
|
|
836
|
+
};
|
|
837
|
+
export const authRedirectCallback = (href, fnLoggedInCB = null) => {
|
|
838
|
+
gAuthMgr.authRedirectCallback(href, fnLoggedInCB);
|
|
839
|
+
};
|
|
840
|
+
/**
|
|
841
|
+
* Silent or visible login based on login status
|
|
842
|
+
* @param {string} appName - unique name for application route (will be used to clear an session storage for another route)
|
|
843
|
+
* @param {boolean} noMainRedirect - avoid the initial main window redirect that happens in scenarios where it is OK to transition
|
|
844
|
+
* away from the main page
|
|
845
|
+
* @param {boolean} deferLogin - defer logging in (if not already authenticated)
|
|
846
|
+
*/
|
|
847
|
+
export const loginIfNecessary = (loginProps) => {
|
|
848
|
+
gAuthMgr.loginIfNecessary(loginProps);
|
|
849
|
+
};
|
|
850
|
+
export const getHomeUrl = () => {
|
|
851
|
+
return `${window.location.origin}/`;
|
|
852
|
+
};
|
|
853
|
+
export const authIsMainRedirect = () => {
|
|
854
|
+
// Even with main redirect, we want to use it only for the first login (so it doesn't wipe out any state or the reload)
|
|
855
|
+
return !gAuthMgr.noInitialRedirect && !gAuthMgr.usePopupForRestOfSession;
|
|
856
|
+
};
|
|
857
|
+
export const sdkIsLoggedIn = () => {
|
|
858
|
+
return gAuthMgr.isLoggedIn;
|
|
859
|
+
};
|
|
860
|
+
export const logout = () => {
|
|
861
|
+
return gAuthMgr.logout();
|
|
862
|
+
};
|
|
863
|
+
// Set the custom authorization header for the SDK (and Constellation JS Engine) to
|
|
864
|
+
// utilize for every DX API request
|
|
865
|
+
export const sdkSetAuthHeader = authHeader => {
|
|
866
|
+
gAuthMgr.bCustomAuth = !!authHeader;
|
|
867
|
+
// Use setter to set this securely
|
|
868
|
+
gAuthMgr.authHeader = authHeader;
|
|
869
|
+
};
|
|
870
|
+
export const getAvailablePortals = async () => {
|
|
871
|
+
return gAuthMgr.getAvailablePortals();
|
|
872
|
+
};
|
|
873
|
+
//# sourceMappingURL=authManager.js.map
|