@pega/react-sdk-overrides 0.23.26 → 8.8.20

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