@pega/react-sdk-overrides 8.8.21 → 8.23.11-debug
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/lib/designSystemExtension/AlertBanner/AlertBanner.tsx +9 -13
- package/lib/designSystemExtension/Banner/Banner.tsx +1 -12
- package/lib/designSystemExtension/CaseSummaryFields/CaseSummaryFields.tsx +10 -15
- package/lib/designSystemExtension/DetailsFields/DetailsFields.tsx +11 -10
- package/lib/designSystemExtension/FieldGroup/FieldGroup.tsx +3 -10
- package/lib/designSystemExtension/FieldGroupList/FieldGroupList.tsx +3 -11
- package/lib/designSystemExtension/FieldValueList/FieldValueList.tsx +3 -11
- package/lib/designSystemExtension/Operator/Operator.tsx +12 -16
- package/lib/designSystemExtension/Pulse/Pulse.tsx +7 -11
- package/lib/designSystemExtension/WssQuickCreate/WssQuickCreate.tsx +1 -10
- package/lib/field/AutoComplete/AutoComplete.tsx +18 -37
- package/lib/field/CancelAlert/CancelAlert.tsx +4 -9
- package/lib/field/Checkbox/Checkbox.tsx +16 -39
- package/lib/field/Currency/Currency.tsx +15 -23
- package/lib/field/Currency/currency-utils.ts +5 -1
- package/lib/field/Date/Date.tsx +15 -22
- package/lib/field/DateTime/DateTime.tsx +14 -25
- package/lib/field/Decimal/Decimal.tsx +14 -25
- package/lib/field/Dropdown/Dropdown.tsx +26 -30
- package/lib/field/Email/Email.tsx +9 -17
- package/lib/field/Integer/Integer.tsx +7 -15
- package/lib/field/Percentage/Percentage.tsx +7 -15
- package/lib/field/Phone/Phone.tsx +11 -19
- package/lib/field/RadioButtons/RadioButtons.tsx +37 -25
- package/lib/field/SemanticLink/SemanticLink.tsx +25 -26
- package/lib/field/TextArea/TextArea.tsx +5 -14
- package/lib/field/TextContent/TextContent.tsx +1 -10
- package/lib/field/TextInput/TextInput.tsx +6 -15
- package/lib/field/Time/Time.tsx +27 -22
- package/lib/field/URL/URL.tsx +8 -16
- package/lib/field/UserReference/UserReference.tsx +60 -50
- package/lib/helpers/attachmentHelpers.ts +4 -4
- package/lib/helpers/auth.js +397 -741
- package/lib/helpers/authManager.js +634 -0
- package/lib/helpers/case-utils.tsx +9 -8
- package/lib/helpers/common-utils.ts +1 -1
- package/lib/helpers/config_access.js +73 -0
- package/lib/helpers/date-format-utils.ts +1 -1
- package/lib/helpers/event-utils.ts +1 -1
- package/lib/helpers/formatters/Currency.ts +11 -11
- package/lib/helpers/formatters/CurrencyMap.ts +5 -8
- package/lib/helpers/formatters/Date.ts +1 -1
- package/lib/helpers/formatters/common.ts +6 -2
- package/lib/helpers/formatters/index.ts +3 -3
- package/lib/helpers/simpleTableHelpers.ts +7 -7
- package/lib/helpers/state-utils.tsx +3 -0
- package/lib/helpers/template-utils.ts +6 -3
- package/lib/helpers/versionHelpers.ts +3 -0
- package/lib/infra/ActionButtons/ActionButtons.tsx +19 -9
- package/lib/infra/Assignment/Assignment.tsx +28 -21
- package/lib/infra/AssignmentCard/AssignmentCard.tsx +19 -15
- package/lib/infra/Containers/FlowContainer/FlowContainer.tsx +58 -57
- package/lib/infra/Containers/FlowContainer/helpers.ts +3 -0
- package/lib/infra/Containers/ModalViewContainer/ModalViewContainer.tsx +19 -14
- package/lib/infra/Containers/ViewContainer/ViewContainer.tsx +25 -14
- package/lib/infra/DashboardFilter/DashboardFilter.tsx +21 -13
- package/lib/infra/DashboardFilter/filterUtils.tsx +1 -1
- package/lib/infra/DeferLoad/DeferLoad.tsx +20 -14
- package/lib/infra/ErrorBoundary/ErrorBoundary.tsx +14 -10
- package/lib/infra/MultiStep/MultiStep.tsx +22 -22
- package/lib/infra/NavBar/NavBar.tsx +21 -14
- package/lib/infra/Reference/Reference.tsx +18 -13
- package/lib/infra/Region/Region.tsx +6 -8
- package/lib/infra/RootContainer/RootContainer.tsx +25 -16
- package/lib/infra/Stages/Stages.tsx +8 -10
- package/lib/infra/VerticalTabs/LeftAlignVerticalTabs/LeftAlignVerticalTabs.tsx +1 -8
- package/lib/infra/VerticalTabs/VerticalTabs/VerticalTabs.tsx +12 -12
- package/lib/infra/View/View.tsx +22 -21
- package/lib/template/AppShell/AppShell.tsx +25 -36
- package/lib/template/BannerPage/BannerPage.tsx +31 -26
- package/lib/template/CaseSummary/CaseSummary.tsx +8 -15
- package/lib/template/CaseView/CaseView.tsx +25 -24
- package/lib/template/CaseViewActionsMenu/CaseViewActionsMenu.tsx +19 -10
- package/lib/template/Confirmation/Confirmation.tsx +53 -27
- package/lib/template/DataReference/DataReference.tsx +52 -48
- package/lib/template/DefaultForm/DefaultForm.tsx +12 -14
- package/lib/template/Details/Details/Details.tsx +17 -16
- package/lib/template/Details/DetailsSubTabs/DetailsSubTabs.tsx +16 -13
- package/lib/template/Details/DetailsThreeColumn/DetailsThreeColumn.tsx +18 -19
- package/lib/template/Details/DetailsTwoColumn/DetailsTwoColumn.tsx +18 -20
- package/lib/template/FieldGroupTemplate/FieldGroupTemplate.tsx +28 -22
- package/lib/template/InlineDashboard/InlineDashboard.tsx +7 -11
- package/lib/template/InlineDashboardPage/InlineDashboardPage.tsx +17 -19
- package/lib/template/ListPage/ListPage.tsx +13 -14
- package/lib/template/ListView/ListView.tsx +186 -189
- package/lib/template/ListView/utils.ts +23 -170
- package/lib/template/MultiReferenceReadOnly/MultiReferenceReadOnly.tsx +19 -10
- package/lib/template/NarrowWide/NarrowWide/NarrowWide.tsx +1 -16
- package/lib/template/NarrowWide/NarrowWideDetails/NarrowWideDetails.tsx +18 -19
- package/lib/template/NarrowWide/NarrowWideForm/NarrowWideForm.tsx +1 -9
- package/lib/template/NarrowWide/NarrowWidePage/NarrowWidePage.tsx +17 -17
- package/lib/template/OneColumn/OneColumn/OneColumn.tsx +7 -8
- package/lib/template/OneColumn/OneColumnPage/OneColumnPage.tsx +10 -10
- package/lib/template/OneColumn/OneColumnTab/OneColumnTab.tsx +7 -5
- package/lib/template/PromotedFilters/PromotedFilters.tsx +14 -16
- package/lib/template/SimpleTable/SimpleTable/SimpleTable.tsx +8 -102
- package/lib/template/SimpleTable/SimpleTableManual/SimpleTableManual.tsx +7 -24
- package/lib/template/SimpleTable/SimpleTableSelect/SimpleTableSelect.tsx +33 -23
- package/lib/template/SingleReferenceReadOnly/SingleReferenceReadOnly.tsx +36 -34
- package/lib/template/SubTabs/SubTabs.tsx +11 -10
- package/lib/template/SubTabs/tabUtils.ts +4 -0
- package/lib/template/TwoColumn/TwoColumn/TwoColumn.tsx +15 -10
- package/lib/template/TwoColumn/TwoColumnPage/TwoColumnPage.tsx +10 -10
- package/lib/template/TwoColumn/TwoColumnTab/TwoColumnTab.tsx +12 -10
- package/lib/template/WideNarrow/WideNarrow/WideNarrow.tsx +3 -17
- package/lib/template/WideNarrow/WideNarrowDetails/WideNarrowDetails.tsx +25 -35
- package/lib/template/WideNarrow/WideNarrowForm/WideNarrowForm.tsx +1 -7
- package/lib/template/WideNarrow/WideNarrowPage/WideNarrowPage.tsx +17 -15
- package/lib/template/WssNavBar/WssNavBar.tsx +1 -18
- package/lib/widget/AppAnnouncement/AppAnnouncement.tsx +21 -13
- package/lib/widget/Attachment/Attachment.tsx +16 -28
- package/lib/widget/CaseHistory/CaseHistory.tsx +10 -12
- package/lib/widget/FileUtility/ActionButtonsForFileUtil/ActionButtonsForFileUtil.tsx +1 -13
- package/lib/widget/FileUtility/FileUtility/FileUtility.tsx +22 -35
- package/lib/widget/Followers/Followers.tsx +11 -10
- package/lib/widget/QuickCreate/QuickCreate.tsx +5 -11
- package/lib/widget/SummaryItem/SummaryItem.tsx +1 -11
- package/lib/widget/SummaryList/SummaryList.tsx +3 -17
- package/lib/widget/ToDo/ToDo.tsx +105 -62
- package/package.json +1 -1
- package/lib/designSystemExtension/RichTextEditor/RichTextEditor.tsx +0 -121
- package/lib/designSystemExtension/RichTextEditor/index.tsx +0 -1
- package/lib/field/RichText/RichText.tsx +0 -93
- package/lib/field/RichText/index.tsx +0 -1
- package/lib/field/ScalarList/ScalarList.tsx +0 -64
- package/lib/field/ScalarList/config-ext.json +0 -8
- package/lib/field/ScalarList/index.tsx +0 -1
- package/lib/helpers/authManager.ts +0 -933
package/lib/helpers/auth.js
CHANGED
|
@@ -1,834 +1,490 @@
|
|
|
1
1
|
class PegaAuth {
|
|
2
|
-
// The properties within config structure are expected to be more static config values that are then
|
|
3
|
-
// used to properly make various OAuth endpoint calls.
|
|
4
|
-
#config = null;
|
|
5
|
-
// Any dynamic state is stored separately in its own structure. If a sessionStorage key is passed in
|
|
6
|
-
// without a Dynamic State key.
|
|
7
|
-
#dynState = {};
|
|
8
|
-
// Current properties within dynState structure:
|
|
9
|
-
// codeVerifier, state, sessionIndex, sessionIndexAttempts, acRedirectUri
|
|
10
|
-
|
|
11
|
-
constructor(ssKeyConfig, ssKeyDynState) {
|
|
12
|
-
if (typeof ssKeyConfig === 'string') {
|
|
13
|
-
this.ssKeyConfig = ssKeyConfig;
|
|
14
|
-
this.ssKeyDynState = ssKeyDynState || `${ssKeyConfig}_DS`;
|
|
15
|
-
this.#reloadConfig();
|
|
16
|
-
} else {
|
|
17
|
-
// object with config structure is passed in
|
|
18
|
-
this.#config = ssKeyConfig;
|
|
19
|
-
this.#dynState = ssKeyDynState;
|
|
20
|
-
}
|
|
21
|
-
this.urlencoded = 'application/x-www-form-urlencoded';
|
|
22
|
-
this.isNode = typeof window === 'undefined';
|
|
23
2
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
this.
|
|
27
|
-
this.
|
|
28
|
-
}
|
|
29
|
-
if (Object.keys(this.#config).length > 0) {
|
|
30
|
-
if (!this.#config.serverType) {
|
|
31
|
-
this.#config.serverType = 'infinity';
|
|
32
|
-
}
|
|
33
|
-
} else {
|
|
34
|
-
throw new Error('invalid config settings');
|
|
35
|
-
}
|
|
3
|
+
constructor(ssKeyConfig) {
|
|
4
|
+
this.ssKeyConfig = ssKeyConfig;
|
|
5
|
+
this.bEncodeSI = false;
|
|
6
|
+
this.reloadConfig();
|
|
36
7
|
}
|
|
37
8
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
9
|
+
reloadConfig() {
|
|
10
|
+
const peConfig = window.sessionStorage.getItem(this.ssKeyConfig);
|
|
11
|
+
let obj = {};
|
|
12
|
+
if( peConfig ) {
|
|
13
|
+
try {
|
|
14
|
+
obj = JSON.parse(peConfig);
|
|
15
|
+
} catch (e) {
|
|
16
|
+
try {
|
|
17
|
+
obj = JSON.parse(window.atob(peConfig));
|
|
18
|
+
} catch(e2) {
|
|
19
|
+
obj = {};
|
|
20
|
+
}
|
|
21
|
+
}
|
|
50
22
|
}
|
|
51
|
-
}
|
|
52
|
-
if (ssKey === this.ssKeyConfig) {
|
|
53
|
-
this.#config = sItem ? obj : {};
|
|
54
|
-
} else {
|
|
55
|
-
this.#dynState = sItem ? obj : {};
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
23
|
|
|
59
|
-
|
|
60
|
-
if (this.ssKeyConfig) {
|
|
61
|
-
this. #reloadSS(this.ssKeyConfig);
|
|
62
|
-
}
|
|
63
|
-
if (this.ssKeyDynState) {
|
|
64
|
-
this.#reloadSS(this.ssKeyDynState);
|
|
65
|
-
}
|
|
24
|
+
this.config = peConfig ? obj : null;
|
|
66
25
|
}
|
|
67
26
|
|
|
68
27
|
#updateConfig() {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
// May not need to write out Config info all the time, but there is a scenario where a
|
|
72
|
-
// non obfuscated value is passed in and then it needs to be obfuscated
|
|
73
|
-
if (this.ssKeyConfig) {
|
|
74
|
-
const sConfig = JSON.stringify(this.#config);
|
|
75
|
-
window.sessionStorage.setItem(this.ssKeyConfig, transform ? btoa(sConfig) : sConfig);
|
|
76
|
-
}
|
|
77
|
-
if (this.ssKeyDynState) {
|
|
78
|
-
const sDynState = JSON.stringify(this.#dynState);
|
|
79
|
-
window.sessionStorage.setItem(this.ssKeyDynState, transform ? btoa(sDynState) : sDynState);
|
|
80
|
-
}
|
|
81
|
-
if( this.#config.fnDynStateChangedCB ) {
|
|
82
|
-
this.#config.fnDynStateChangedCB();
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
async #importSingleLib(libName, libProp, bLoadAlways = false) {
|
|
87
|
-
// eslint-disable-next-line no-undef
|
|
88
|
-
if (!bLoadAlways && typeof (this.isNode ? global : window)[libProp] !== 'undefined') {
|
|
89
|
-
// eslint-disable-next-line no-undef
|
|
90
|
-
this[libProp] = (this.isNode ? global : window)[libProp];
|
|
91
|
-
return this[libProp];
|
|
92
|
-
}
|
|
93
|
-
// Needed to explicitly make import argument a string by using template literals to fix a compile
|
|
94
|
-
// error: Critical dependency: the request of a dependency is an expression
|
|
95
|
-
return import(`${libName}`)
|
|
96
|
-
.then((mod) => {
|
|
97
|
-
this[libProp] = mod.default;
|
|
98
|
-
})
|
|
99
|
-
.catch((e) => {
|
|
100
|
-
// eslint-disable-next-line no-console
|
|
101
|
-
console.error(`Library ${libName} failed to load. ${e}`);
|
|
102
|
-
throw e;
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
async #importNodeLibs() {
|
|
107
|
-
// Also current assumption is using Node 18 or better
|
|
108
|
-
// With 18.3 there is now a native fetch (but may want to force use of node-fetch)
|
|
109
|
-
const useNodeFetch = !!this.#config.useNodeFetch;
|
|
110
|
-
|
|
111
|
-
return Promise.all([
|
|
112
|
-
this.#importSingleLib('node-fetch', 'fetch', useNodeFetch),
|
|
113
|
-
this.#importSingleLib('open', 'open'),
|
|
114
|
-
this.#importSingleLib('node:crypto', 'crypto', true),
|
|
115
|
-
this.#importSingleLib('node:https', 'https'),
|
|
116
|
-
this.#importSingleLib('node:http', 'http'),
|
|
117
|
-
this.#importSingleLib('node:fs', 'fs')
|
|
118
|
-
]).then(() => {
|
|
119
|
-
this.subtle = this.crypto?.subtle || this.crypto.webcrypto.subtle;
|
|
120
|
-
if ((typeof fetch === 'undefined' || useNodeFetch) && this.fetch) {
|
|
121
|
-
/* eslint-disable-next-line no-global-assign */
|
|
122
|
-
fetch = this.fetch;
|
|
123
|
-
}
|
|
124
|
-
});
|
|
28
|
+
const sSI = JSON.stringify(this.config);
|
|
29
|
+
window.sessionStorage.setItem(this.ssKeyConfig, this.bEncodeSI ? window.btoa(sSI) : sSI);
|
|
125
30
|
}
|
|
126
31
|
|
|
127
32
|
// For PKCE the authorize includes a code_challenge & code_challenge_method as well
|
|
128
33
|
async #buildAuthorizeUrl(state) {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
redirectUri,
|
|
133
|
-
authorizeUri,
|
|
134
|
-
authService,
|
|
135
|
-
appAlias,
|
|
136
|
-
userIdentifier,
|
|
137
|
-
password,
|
|
138
|
-
noPKCE,
|
|
139
|
-
isolationId
|
|
140
|
-
} = this.#config;
|
|
141
|
-
const {
|
|
142
|
-
sessionIndex,
|
|
143
|
-
} = this.#dynState;
|
|
144
|
-
const bInfinity = serverType === 'infinity';
|
|
145
|
-
|
|
146
|
-
if (!noPKCE) {
|
|
34
|
+
const {clientId, redirectUri, authorizeUri, authService, sessionIndex, appAlias, useLocking,
|
|
35
|
+
userIdentifier, password} = this.config;
|
|
36
|
+
|
|
147
37
|
// Generate random string of 64 chars for verifier. RFC 7636 says from 43-128 chars
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
this
|
|
151
|
-
|
|
38
|
+
let buf = new Uint8Array(64);
|
|
39
|
+
window.crypto.getRandomValues(buf);
|
|
40
|
+
this.config.codeVerifier = this.#base64UrlSafeEncode(buf);
|
|
41
|
+
// If sessionIndex exists then increment attempts count (we will stop sending session_index after two failures)
|
|
42
|
+
if( sessionIndex ) {
|
|
43
|
+
this.config.sessionIndexAttempts += 1;
|
|
44
|
+
}
|
|
45
|
+
// Persist codeVerifier in session storage so it survives the redirects that are to follow
|
|
46
|
+
this.#updateConfig();
|
|
47
|
+
|
|
48
|
+
if( !state ) {
|
|
49
|
+
// Calc random state variable
|
|
50
|
+
buf = new Uint8Array(32);
|
|
51
|
+
window.crypto.getRandomValues(buf);
|
|
52
|
+
state = this.#base64UrlSafeEncode(buf);
|
|
53
|
+
}
|
|
152
54
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
if (sessionIndex) {
|
|
156
|
-
this.#dynState.sessionIndexAttempts += 1;
|
|
157
|
-
}
|
|
55
|
+
// Trim alias to include just the real alias piece
|
|
56
|
+
const addtlScope = appAlias ? `+app.alias.${appAlias.replace(/^app\//, '')}` : "";
|
|
158
57
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
// Persist codeVerifier in session storage so it survives the redirects that are to follow
|
|
168
|
-
this.#updateConfig();
|
|
169
|
-
|
|
170
|
-
// Trim alias to include just the real alias piece
|
|
171
|
-
const addtlScope = appAlias ? `+app.alias.${appAlias.replace(/^app\//, '')}` : '';
|
|
172
|
-
const scope = bInfinity ? `openid${addtlScope}` : 'user_info';
|
|
173
|
-
|
|
174
|
-
// Add explicit creds if specified to try to avoid login popup
|
|
175
|
-
const authServiceArg = authService ? `&authentication_service=${encodeURIComponent(authService)}` : '';
|
|
176
|
-
const sessionIndexArg =
|
|
177
|
-
sessionIndex && this.#dynState.sessionIndexAttempts < 3 ? `&session_index=${sessionIndex}` : '';
|
|
178
|
-
const userIdentifierArg = userIdentifier ? `&UserIdentifier=${encodeURIComponent(userIdentifier)}` : '';
|
|
179
|
-
const passwordArg = password && userIdentifier ? `&Password=${encodeURIComponent(atob(password))}` : '';
|
|
180
|
-
const moreAuthArgs = bInfinity
|
|
181
|
-
? `&enable_psyncId=true${authServiceArg}${sessionIndexArg}${userIdentifierArg}${passwordArg}`
|
|
182
|
-
: `&isolationID=${isolationId}`;
|
|
183
|
-
|
|
184
|
-
let pkceArgs = '';
|
|
185
|
-
if (!noPKCE) {
|
|
186
|
-
const cc = await this.#getCodeChallenge(this.#dynState.codeVerifier);
|
|
187
|
-
pkceArgs = `&code_challenge=${cc}&code_challenge_method=S256`;
|
|
188
|
-
}
|
|
189
|
-
return `${authorizeUri}?client_id=${clientId}&response_type=code&redirect_uri=${redirectUri}&scope=${scope}&state=${
|
|
190
|
-
this.#dynState.state
|
|
191
|
-
}${pkceArgs}${moreAuthArgs}`;
|
|
192
|
-
}
|
|
58
|
+
// Add explicit creds if specified to try to avoid login popup
|
|
59
|
+
const moreAuthArgs =
|
|
60
|
+
(authService ? `&authentication_service=${encodeURIComponent(authService)}` : "") +
|
|
61
|
+
(sessionIndex && this.config.sessionIndexAttempts < 3 ? `&session_index=${sessionIndex}` : "") +
|
|
62
|
+
(useLocking ? `&enable_psyncId=true` : '') +
|
|
63
|
+
(userIdentifier ? `&UserIdentifier=${encodeURIComponent(userIdentifier)}` : '') +
|
|
64
|
+
(userIdentifier && password ? `&Password=${encodeURIComponent(window.atob(password))}` : '');
|
|
193
65
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
}
|
|
199
|
-
const { grantType, noPKCE } = this.#config;
|
|
200
|
-
if (grantType && grantType !== 'authCode') {
|
|
201
|
-
return this.getToken();
|
|
202
|
-
}
|
|
203
|
-
// Make sure browser in a secure context, else PKCE will fail
|
|
204
|
-
if (!this.isNode && !noPKCE && !window.isSecureContext) {
|
|
205
|
-
throw new Error(
|
|
206
|
-
`Authorization code grant flow failed due to insecure browser context at ${window.location.origin}. Use localhost or https.`
|
|
207
|
-
);
|
|
208
|
-
}
|
|
209
|
-
return this.#authCodeStart();
|
|
66
|
+
return this.#getCodeChallenge(this.config.codeVerifier).then( cc => {
|
|
67
|
+
// Now includes new enable_psyncId=true and session_index params
|
|
68
|
+
return `${authorizeUri}?client_id=${clientId}&response_type=code&redirect_uri=${redirectUri}&scope=openid+email+profile${addtlScope}&state=${state}&code_challenge=${cc}&code_challenge_method=S256${moreAuthArgs}`;
|
|
69
|
+
});
|
|
210
70
|
}
|
|
211
71
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
return nFirstPathOffset !== -1 ? redirectUri.substring(0, nFirstPathOffset) : redirectUri;
|
|
219
|
-
};
|
|
220
|
-
|
|
221
|
-
const redirectOrigin = fnGetRedirectUriOrigin();
|
|
222
|
-
const state = this.isNode ? '' : btoa(window.location.origin);
|
|
223
|
-
|
|
224
|
-
return new Promise((resolve, reject) => {
|
|
225
|
-
let theUrl = null; // holds the crafted authorize url
|
|
226
|
-
|
|
227
|
-
let myWindow = null; // popup or iframe
|
|
228
|
-
let elIframe = null;
|
|
229
|
-
let elCloseBtn = null;
|
|
230
|
-
const iframeTimeout = this.#config.silentTimeout !== undefined ? this.#config.silentTimeout : 5000;
|
|
231
|
-
let bWinIframe = true;
|
|
232
|
-
let tmrAuthComplete = null;
|
|
233
|
-
let checkWindowClosed = null;
|
|
234
|
-
let bDisablePromptNone = false;
|
|
235
|
-
const myWinOnLoad = () => {
|
|
236
|
-
try {
|
|
237
|
-
if (bWinIframe) {
|
|
238
|
-
elIframe.contentWindow.postMessage({ type: 'PegaAuth' }, redirectOrigin);
|
|
239
|
-
} else {
|
|
240
|
-
myWindow.postMessage({ type: 'PegaAuth' }, redirectOrigin);
|
|
241
|
-
}
|
|
242
|
-
} catch (e) {
|
|
243
|
-
// Exception trying to postMessage on load (perhaps should console.warn)
|
|
244
|
-
}
|
|
245
|
-
};
|
|
246
|
-
const fnSetSilentAuthFailed = (bSet) => {
|
|
247
|
-
this.#config.silentAuthFailed = bSet;
|
|
248
|
-
this.#updateConfig();
|
|
249
|
-
};
|
|
250
|
-
/* eslint-disable prefer-promise-reject-errors */
|
|
251
|
-
const fnOpenPopup = () => {
|
|
252
|
-
if (this.#config.noPopups) {
|
|
253
|
-
return reject('no-popups');
|
|
254
|
-
}
|
|
255
|
-
// Since displaying a visible window, clear the silent auth failed flag
|
|
256
|
-
fnSetSilentAuthFailed(false);
|
|
257
|
-
myWindow = (this.isNode ? this.open : window.open)(theUrl, '_blank', 'width=700,height=500,left=200,top=100');
|
|
258
|
-
if (!myWindow) {
|
|
259
|
-
// Blocked by popup-blocker
|
|
260
|
-
return reject('blocked');
|
|
261
|
-
}
|
|
262
|
-
checkWindowClosed = setInterval(() => {
|
|
263
|
-
if (myWindow.closed) {
|
|
264
|
-
clearInterval(checkWindowClosed);
|
|
265
|
-
reject('closed');
|
|
266
|
-
}
|
|
267
|
-
}, 500);
|
|
268
|
-
if (!this.isNode) {
|
|
269
|
-
try {
|
|
270
|
-
myWindow.addEventListener('load', myWinOnLoad, true);
|
|
271
|
-
} catch (e) {
|
|
272
|
-
// Exception trying to add onload handler to opened window
|
|
273
|
-
// eslint-disable-next-line no-console
|
|
274
|
-
console.error(`Error adding event listener on popup window: ${e}`);
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
};
|
|
278
|
-
|
|
279
|
-
/* eslint-enable prefer-promise-reject-errors */
|
|
280
|
-
const fnCloseIframe = () => {
|
|
281
|
-
elIframe.parentNode.removeChild(elIframe);
|
|
282
|
-
elCloseBtn.parentNode.removeChild(elCloseBtn);
|
|
283
|
-
elIframe = null;
|
|
284
|
-
elCloseBtn = null;
|
|
285
|
-
bWinIframe = false;
|
|
286
|
-
};
|
|
287
|
-
|
|
288
|
-
const fnCloseAndReject = () => {
|
|
289
|
-
fnCloseIframe();
|
|
290
|
-
/* eslint-disable-next-line prefer-promise-reject-errors */
|
|
291
|
-
reject('closed');
|
|
292
|
-
};
|
|
293
|
-
|
|
294
|
-
const fnAuthMessageReceiver = (event) => {
|
|
295
|
-
// Check origin to make sure it is the redirect origin
|
|
296
|
-
if (event.origin !== redirectOrigin) return;
|
|
297
|
-
if (!event.data || !event.data.type || event.data.type !== 'PegaAuth') return;
|
|
298
|
-
const aArgs = ['code', 'state', 'error', 'errorDesc'];
|
|
299
|
-
const aValues = [];
|
|
300
|
-
for (let i = 0; i < aArgs.length; i += 1) {
|
|
301
|
-
const arg = aArgs[i];
|
|
302
|
-
aValues[arg] = event.data[arg] ? event.data[arg].toString() : null;
|
|
303
|
-
}
|
|
304
|
-
if (aValues.error || (aValues.code && aValues.state === this.#dynState.state)) {
|
|
305
|
-
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
|
306
|
-
fnGetTokenAndFinish(aValues.code, aValues.error, aValues.errorDesc);
|
|
307
|
-
}
|
|
308
|
-
};
|
|
309
|
-
|
|
310
|
-
const fnEnableMessageReceiver = (bEnable) => {
|
|
311
|
-
if (bEnable) {
|
|
312
|
-
window.addEventListener('message', fnAuthMessageReceiver, false);
|
|
313
|
-
window.authCodeCallback = (code, state1, error, errorDesc) => {
|
|
314
|
-
if (error || (code && state1 === this.#dynState.state)) {
|
|
315
|
-
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
|
316
|
-
fnGetTokenAndFinish(code, error, errorDesc);
|
|
317
|
-
}
|
|
318
|
-
};
|
|
319
|
-
} else {
|
|
320
|
-
window.removeEventListener('message', fnAuthMessageReceiver, false);
|
|
321
|
-
delete window.authCodeCallback;
|
|
322
|
-
}
|
|
72
|
+
async login() {
|
|
73
|
+
const fnGetRedirectUriOrigin = () => {
|
|
74
|
+
const redirectUri = this.config.redirectUri;
|
|
75
|
+
const nRootOffset = redirectUri.indexOf("//");
|
|
76
|
+
const nFirstPathOffset = nRootOffset !== -1 ? redirectUri.indexOf("/",nRootOffset+2) : -1;
|
|
77
|
+
return nFirstPathOffset !== -1 ? redirectUri.substring(0,nFirstPathOffset) : redirectUri;
|
|
323
78
|
};
|
|
324
79
|
|
|
325
|
-
const
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
80
|
+
const redirectOrigin = fnGetRedirectUriOrigin();
|
|
81
|
+
// eslint-disable-next-line no-restricted-globals
|
|
82
|
+
const state = window.btoa(location.origin);
|
|
83
|
+
|
|
84
|
+
return new Promise( (resolve, reject) => {
|
|
85
|
+
|
|
86
|
+
this.#buildAuthorizeUrl(state).then((url) => {
|
|
87
|
+
let myWindow = null; // popup or iframe
|
|
88
|
+
let elIframe = null;
|
|
89
|
+
let elCloseBtn = null
|
|
90
|
+
const iframeTimeout = this.config.silentTimeout !== undefined ? this.config.silentTimeout : 5000;
|
|
91
|
+
let bWinIframe = iframeTimeout > 0 && ((!!this.config.userIdentifier && !!this.config.password) || this.config.iframeLoginUI || this.config.authService !== "pega");
|
|
92
|
+
let tmrAuthComplete = null;
|
|
93
|
+
let checkWindowClosed = null;
|
|
94
|
+
const myWinOnLoad = () => {
|
|
95
|
+
try{
|
|
96
|
+
if( bWinIframe ) {
|
|
97
|
+
elIframe.contentWindow.postMessage({type:"PegaAuth"}, redirectOrigin);
|
|
98
|
+
// eslint-disable-next-line no-console
|
|
99
|
+
console.log("authjs(login): loaded a page in iFrame");
|
|
100
|
+
} else {
|
|
101
|
+
myWindow.postMessage({type:"PegaAuth"}, redirectOrigin);
|
|
102
|
+
}
|
|
103
|
+
} catch(e) {
|
|
104
|
+
// eslint-disable-next-line no-console
|
|
105
|
+
console.log("authjs(login): Exception trying to postMessage on load");
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
const fnOpenPopup = () => {
|
|
109
|
+
myWindow = window.open(url, '_blank', 'width=700,height=500,left=200,top=100');
|
|
110
|
+
if( !myWindow ) {
|
|
111
|
+
// Blocked by popup-blocker
|
|
112
|
+
// eslint-disable-next-line prefer-promise-reject-errors
|
|
113
|
+
return reject("blocked");
|
|
114
|
+
}
|
|
115
|
+
checkWindowClosed = setInterval( () => {
|
|
116
|
+
if( myWindow.closed ) {
|
|
117
|
+
clearInterval(checkWindowClosed);
|
|
118
|
+
// eslint-disable-next-line prefer-promise-reject-errors
|
|
119
|
+
reject("closed");
|
|
120
|
+
}
|
|
121
|
+
}, 500);
|
|
122
|
+
try {
|
|
123
|
+
myWindow.addEventListener("load", myWinOnLoad, true);
|
|
124
|
+
} catch(e) {
|
|
125
|
+
// eslint-disable-next-line no-console
|
|
126
|
+
console.log("authjs(login): Exception trying to add onload handler to opened window;")
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const fnCloseIframe = () => {
|
|
131
|
+
elIframe.parentNode.removeChild(elIframe);
|
|
132
|
+
elCloseBtn.parentNode.removeChild(elCloseBtn);
|
|
133
|
+
// eslint-disable-next-line no-multi-assign
|
|
134
|
+
elIframe = elCloseBtn = null;
|
|
135
|
+
bWinIframe = false;
|
|
136
|
+
};
|
|
137
|
+
const fnCloseAndReject = () => {
|
|
138
|
+
fnCloseIframe();
|
|
139
|
+
// eslint-disable-next-line prefer-promise-reject-errors
|
|
140
|
+
reject("closed");
|
|
141
|
+
};
|
|
142
|
+
// If there is a userIdentifier and password specified or an external SSO auth service,
|
|
143
|
+
// we can try to use this silently in an iFrame first
|
|
144
|
+
if( bWinIframe ) {
|
|
145
|
+
const nFrameZLevel = 99999;
|
|
146
|
+
elIframe = document.createElement('iframe');
|
|
147
|
+
// eslint-disable-next-line prefer-template
|
|
148
|
+
elIframe.id = 'pe'+this.config.clientId;
|
|
149
|
+
const loginBoxWidth=500;
|
|
150
|
+
const loginBoxHeight=700;
|
|
151
|
+
const oStyle = elIframe.style;
|
|
152
|
+
oStyle.position = 'absolute';
|
|
153
|
+
oStyle.display = 'none';
|
|
154
|
+
oStyle.zIndex = nFrameZLevel;
|
|
155
|
+
oStyle.top=`${Math.round(Math.max(window.innerHeight-loginBoxHeight,0)/2)}px`;
|
|
156
|
+
oStyle.left=`${Math.round(Math.max(window.innerWidth-loginBoxWidth,0)/2)}px`;
|
|
157
|
+
oStyle.width='500px';
|
|
158
|
+
oStyle.height='700px';
|
|
159
|
+
// Add Iframe to top of document DOM to have it load
|
|
160
|
+
document.body.insertBefore(elIframe,document.body.firstChild);
|
|
161
|
+
// Add Iframe to DOM to have it load
|
|
162
|
+
document.getElementsByTagName('body')[0].appendChild(elIframe);
|
|
163
|
+
elIframe.addEventListener("load", myWinOnLoad, true);
|
|
164
|
+
// Disallow iframe content attempts to navigate main window
|
|
165
|
+
elIframe.setAttribute("sandbox","allow-scripts allow-forms allow-same-origin");
|
|
166
|
+
elIframe.setAttribute('src', url);
|
|
167
|
+
|
|
168
|
+
const svgCloseBtn =
|
|
169
|
+
`<?xml version="1.0" encoding="UTF-8"?>
|
|
170
|
+
<svg width="34px" height="34px" viewBox="0 0 34 34" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
171
|
+
<title>Dismiss - Black</title>
|
|
172
|
+
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
|
173
|
+
<g transform="translate(1.000000, 1.000000)">
|
|
174
|
+
<circle fill="#252C32" cx="16" cy="16" r="16"></circle>
|
|
175
|
+
<g transform="translate(9.109375, 9.214844)" fill="#FFFFFF" fill-rule="nonzero">
|
|
176
|
+
<path d="M12.7265625,0 L0,12.6210938 L1.0546875,13.5703125 L13.78125,1.0546875 L12.7265625,0 Z M13.7460938,12.5507812 L1.01953125,0 L0,1.01953125 L12.7617188,13.6054688 L13.7460938,12.5507812 Z"></path>
|
|
177
|
+
</g>
|
|
178
|
+
</g>
|
|
179
|
+
</g>
|
|
180
|
+
</svg>`;
|
|
181
|
+
const bCloseWithinFrame = false;
|
|
182
|
+
elCloseBtn = document.createElement('img');
|
|
183
|
+
elCloseBtn.onclick = fnCloseAndReject;
|
|
184
|
+
// eslint-disable-next-line prefer-template
|
|
185
|
+
elCloseBtn.src = 'data:image/svg+xml;base64,' + window.btoa(svgCloseBtn);
|
|
186
|
+
const oBtnStyle = elCloseBtn.style;
|
|
187
|
+
oBtnStyle.cursor = 'pointer';
|
|
188
|
+
// If svg doesn't set width and height might want to set oBtStyle width and height to something like '2em'
|
|
189
|
+
oBtnStyle.position = 'absolute';
|
|
190
|
+
oBtnStyle.display = 'none';
|
|
191
|
+
oBtnStyle.zIndex = nFrameZLevel+1;
|
|
192
|
+
const nTopOffset = bCloseWithinFrame ? 5 : -10;
|
|
193
|
+
const nRightOffset = bCloseWithinFrame ? -34 : -20;
|
|
194
|
+
oBtnStyle.top = `${Math.round(Math.max(window.innerHeight-loginBoxHeight,0)/2)+nTopOffset}px`;
|
|
195
|
+
oBtnStyle.left = `${Math.round(Math.max(window.innerWidth-loginBoxWidth,0)/2)+loginBoxWidth+nRightOffset}px`;
|
|
196
|
+
document.body.insertBefore(elCloseBtn,document.body.firstChild);
|
|
197
|
+
// If the password was wrong, then the login screen will be in the iframe
|
|
198
|
+
// ..and with Pega without realization of US-372314 it may replace the top (main portal) window
|
|
199
|
+
// For now set a timer and if the timer expires, remove the iFrame and use same url within
|
|
200
|
+
// visible window
|
|
201
|
+
tmrAuthComplete = setTimeout( () => {
|
|
202
|
+
clearTimeout(tmrAuthComplete);
|
|
203
|
+
// remove password from config
|
|
204
|
+
if( this.config.password ) {
|
|
205
|
+
delete this.config.password;
|
|
206
|
+
this.#updateConfig();
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if( this.config.iframeLoginUI ) {
|
|
210
|
+
elIframe.style.display="block";
|
|
211
|
+
elCloseBtn.style.display="block";
|
|
212
|
+
} else {
|
|
213
|
+
fnCloseIframe();
|
|
214
|
+
fnOpenPopup();
|
|
427
215
|
}
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
})
|
|
478
|
-
.catch((e) => {
|
|
479
|
-
reject(e);
|
|
480
|
-
});
|
|
481
|
-
} else if (error) {
|
|
482
|
-
// Handle some errors in a special manner and pass others back to client
|
|
483
|
-
if (error === 'login_required') {
|
|
484
|
-
// eslint-disable-next-line no-console
|
|
485
|
-
console.warn('silent authentication failed...starting full authentication');
|
|
486
|
-
const bSpecialDebugPath = false;
|
|
487
|
-
if (bSpecialDebugPath) {
|
|
488
|
-
fnSetSilentAuthFailed(false);
|
|
489
|
-
bDisablePromptNone = true;
|
|
490
|
-
} else {
|
|
491
|
-
fnSetSilentAuthFailed(true);
|
|
492
|
-
bDisablePromptNone = false;
|
|
493
|
-
}
|
|
494
|
-
this.#buildAuthorizeUrl(state).then((url) => {
|
|
495
|
-
theUrl = url;
|
|
496
|
-
doAuthorize();
|
|
497
|
-
});
|
|
498
|
-
} else if (error === 'invalid_session_index') {
|
|
499
|
-
// eslint-disable-next-line no-console
|
|
500
|
-
console.warn('auth session no longer valid...starting new session');
|
|
501
|
-
// In these scenarios, not much user can do without just starting a new session, so do that
|
|
502
|
-
this.#updateSessionIndex(null);
|
|
503
|
-
fnSetSilentAuthFailed(false);
|
|
504
|
-
this.#buildAuthorizeUrl(state).then((url) => {
|
|
505
|
-
theUrl = url;
|
|
506
|
-
doAuthorize();
|
|
507
|
-
});
|
|
508
|
-
} else {
|
|
509
|
-
// eslint-disable-next-line no-console
|
|
510
|
-
console.warn(`Authorize failed: ${error}. ${errorDesc}\nFailing authorize url: ${theUrl}`);
|
|
511
|
-
throw new Error(error, { cause: errorDesc });
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
};
|
|
515
|
-
|
|
516
|
-
this.#buildAuthorizeUrl(state).then((url) => {
|
|
517
|
-
theUrl = url;
|
|
518
|
-
doAuthorize();
|
|
216
|
+
}, iframeTimeout);
|
|
217
|
+
} else {
|
|
218
|
+
fnOpenPopup();
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
let authMessageReceiver = null;
|
|
222
|
+
/* Retrieve token(s) and close login window */
|
|
223
|
+
const fnGetTokenAndFinish = (code) => {
|
|
224
|
+
window.removeEventListener("message", authMessageReceiver, false);
|
|
225
|
+
this.getToken(code).then(token => {
|
|
226
|
+
if( bWinIframe ) {
|
|
227
|
+
clearTimeout(tmrAuthComplete);
|
|
228
|
+
fnCloseIframe();
|
|
229
|
+
} else {
|
|
230
|
+
clearInterval(checkWindowClosed);
|
|
231
|
+
try {
|
|
232
|
+
myWindow.close();
|
|
233
|
+
} catch (e) {
|
|
234
|
+
// eslint-disable-next-line no-console
|
|
235
|
+
console.warn(`attempt to close opened window failed`);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
resolve(token);
|
|
239
|
+
})
|
|
240
|
+
.catch(e => {
|
|
241
|
+
reject(e);
|
|
242
|
+
});
|
|
243
|
+
};
|
|
244
|
+
/* Handler to receive the auth code */
|
|
245
|
+
authMessageReceiver = (event) => {
|
|
246
|
+
// Check origin to make sure it is the redirect origin
|
|
247
|
+
if( event.origin !== redirectOrigin ) {
|
|
248
|
+
// eslint-disable-next-line no-console
|
|
249
|
+
console.info(`Received event from unexpected origin: ${event.origin} (was expecting: ${redirectOrigin})`);
|
|
250
|
+
}
|
|
251
|
+
if( !event.data || !event.data.type || event.data.type !== "PegaAuth" )
|
|
252
|
+
return;
|
|
253
|
+
// eslint-disable-next-line no-console
|
|
254
|
+
console.log("authjs(login): postMessage received with code");
|
|
255
|
+
const code = event.data.code.toString();
|
|
256
|
+
fnGetTokenAndFinish(code);
|
|
257
|
+
};
|
|
258
|
+
window.addEventListener("message", authMessageReceiver, false);
|
|
259
|
+
window.authCodeCallback = (code) => {
|
|
260
|
+
// eslint-disable-next-line no-console
|
|
261
|
+
console.log("authjs(login): authCodeCallback used with code");
|
|
262
|
+
fnGetTokenAndFinish(code);
|
|
263
|
+
};
|
|
264
|
+
});
|
|
519
265
|
});
|
|
520
|
-
});
|
|
521
266
|
}
|
|
522
267
|
|
|
523
268
|
// Login redirect
|
|
524
269
|
loginRedirect() {
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
// check state
|
|
534
|
-
checkStateMatch(state) {
|
|
535
|
-
return state === this.#dynState.state;
|
|
270
|
+
// eslint-disable-next-line no-restricted-globals
|
|
271
|
+
const state = btoa(location.origin);
|
|
272
|
+
this.#buildAuthorizeUrl(state).then((url) => {
|
|
273
|
+
// eslint-disable-next-line no-restricted-globals
|
|
274
|
+
location.href = url;
|
|
275
|
+
});
|
|
536
276
|
}
|
|
537
277
|
|
|
538
|
-
// Clear session index within config
|
|
539
|
-
#updateSessionIndex(sessionIndex) {
|
|
540
|
-
if (sessionIndex) {
|
|
541
|
-
this.#dynState.sessionIndex = sessionIndex;
|
|
542
|
-
this.#dynState.sessionIndexAttempts = 0;
|
|
543
|
-
} else if (this.#dynState.sessionIndex) {
|
|
544
|
-
delete this.#dynState.sessionIndex;
|
|
545
|
-
}
|
|
546
|
-
this.#updateConfig();
|
|
547
|
-
}
|
|
548
278
|
|
|
549
279
|
// For PKCE token endpoint includes code_verifier
|
|
550
280
|
getToken(authCode) {
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
const {
|
|
555
|
-
serverType,
|
|
556
|
-
isolationId,
|
|
557
|
-
clientId,
|
|
558
|
-
clientSecret,
|
|
559
|
-
tokenUri,
|
|
560
|
-
grantType,
|
|
561
|
-
customTokenParams,
|
|
562
|
-
userIdentifier,
|
|
563
|
-
password,
|
|
564
|
-
noPKCE
|
|
565
|
-
} = this.#config;
|
|
566
|
-
|
|
567
|
-
const {
|
|
568
|
-
sessionIndex,
|
|
569
|
-
acRedirectUri,
|
|
570
|
-
codeVerifier
|
|
571
|
-
} = this.#dynState;
|
|
572
|
-
|
|
573
|
-
const bAuthCode = !grantType || grantType === 'authCode';
|
|
574
|
-
if (bAuthCode && !authCode && !this.isNode) {
|
|
575
|
-
const queryString = window.location.search;
|
|
576
|
-
const urlParams = new URLSearchParams(queryString);
|
|
577
|
-
authCode = urlParams.get('code');
|
|
578
|
-
}
|
|
281
|
+
// Reload config to pick up the previously stored codeVerifier
|
|
282
|
+
this.reloadConfig();
|
|
579
283
|
|
|
580
|
-
|
|
581
|
-
formData.append('client_id', clientId);
|
|
582
|
-
if (clientSecret) {
|
|
583
|
-
formData.append('client_secret', clientSecret);
|
|
584
|
-
}
|
|
585
|
-
/* eslint-disable camelcase */
|
|
586
|
-
const fullGTName = {
|
|
587
|
-
authCode: 'authorization_code',
|
|
588
|
-
clientCreds: 'client_credentials',
|
|
589
|
-
customBearer: 'custom-bearer',
|
|
590
|
-
passwordCreds: 'password'
|
|
591
|
-
}[grantType];
|
|
592
|
-
const grant_type = fullGTName || grantType || 'authorization_code';
|
|
593
|
-
formData.append('grant_type', grant_type);
|
|
594
|
-
if (serverType === 'launchpad' && grantType !== 'authCode') {
|
|
595
|
-
formData.append('isolation_ids', isolationId);
|
|
596
|
-
}
|
|
597
|
-
if (bAuthCode) {
|
|
598
|
-
formData.append('code', authCode);
|
|
599
|
-
formData.append('redirect_uri', acRedirectUri);
|
|
600
|
-
if (!noPKCE) {
|
|
601
|
-
formData.append('code_verifier', codeVerifier);
|
|
602
|
-
}
|
|
603
|
-
} else if (sessionIndex) {
|
|
604
|
-
formData.append('session_index', sessionIndex);
|
|
605
|
-
}
|
|
606
|
-
/* eslint-enable camelcase */
|
|
607
|
-
if (grantType === 'customBearer' && customTokenParams) {
|
|
608
|
-
Object.keys(customTokenParams).forEach((param) => {
|
|
609
|
-
formData.append(param, customTokenParams[param]);
|
|
610
|
-
});
|
|
611
|
-
}
|
|
612
|
-
if (grantType !== 'authCode') {
|
|
613
|
-
formData.append('enable_psyncId', 'true');
|
|
614
|
-
}
|
|
615
|
-
if (grantType === 'passwordCreds') {
|
|
616
|
-
formData.append('username', userIdentifier);
|
|
617
|
-
formData.append('password', atob(password));
|
|
618
|
-
}
|
|
284
|
+
const {clientId, clientSecret, redirectUri, tokenUri, codeVerifier} = this.config;
|
|
619
285
|
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
'content-type': this.urlencoded
|
|
625
|
-
}),
|
|
286
|
+
// eslint-disable-next-line no-restricted-globals
|
|
287
|
+
const queryString = location.search;
|
|
288
|
+
const urlParams = new URLSearchParams(queryString);
|
|
289
|
+
const code = authCode || urlParams.get("code");
|
|
626
290
|
|
|
627
|
-
|
|
628
|
-
|
|
291
|
+
const formData = new URLSearchParams();
|
|
292
|
+
formData.append("client_id", clientId);
|
|
293
|
+
if( clientSecret ) {
|
|
294
|
+
formData.append("client_secret", clientSecret);
|
|
295
|
+
}
|
|
296
|
+
formData.append("grant_type", "authorization_code");
|
|
297
|
+
formData.append("code", code);
|
|
298
|
+
formData.append("redirect_uri", redirectUri);
|
|
299
|
+
formData.append("code_verifier", codeVerifier);
|
|
300
|
+
|
|
301
|
+
return fetch(tokenUri, {
|
|
302
|
+
method: "POST",
|
|
303
|
+
headers: new Headers({
|
|
304
|
+
"content-type": "application/x-www-form-urlencoded",
|
|
305
|
+
}),
|
|
306
|
+
|
|
307
|
+
body: formData.toString(),
|
|
308
|
+
})
|
|
629
309
|
.then((response) => response.json())
|
|
630
|
-
.then(
|
|
631
|
-
if (token.errors || token.error) {
|
|
632
|
-
// eslint-disable-next-line no-console
|
|
633
|
-
console.error(`Token endpoint error: ${JSON.stringify(token.errors || token.error)}`);
|
|
634
|
-
} else {
|
|
310
|
+
.then(token => {
|
|
635
311
|
// .expires_in contains the # of seconds before access token expires
|
|
636
312
|
// add property to keep track of current time when the token expires
|
|
637
|
-
token.eA = Date.now() + token.expires_in * 1000;
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
delete this.#dynState.state;
|
|
641
|
-
}
|
|
642
|
-
if (this.#dynState.codeVerifier) {
|
|
643
|
-
delete this.#dynState.codeVerifier;
|
|
644
|
-
}
|
|
645
|
-
if (this.#dynState.acRedirectUri) {
|
|
646
|
-
delete this.#dynState.acRedirectUri;
|
|
313
|
+
token.eA = Date.now() + (token.expires_in * 1000);
|
|
314
|
+
if( this.config.codeVerifier ) {
|
|
315
|
+
delete this.config.codeVerifier;
|
|
647
316
|
}
|
|
648
317
|
// If there is a session_index then move this to the peConfig structure (as used on authorize)
|
|
649
|
-
if
|
|
650
|
-
|
|
318
|
+
if( token.session_index ) {
|
|
319
|
+
this.config.sessionIndex = token.session_index;
|
|
651
320
|
}
|
|
652
321
|
// If we got a token and have a session index, then reset the sessionIndexAttempts
|
|
653
|
-
if
|
|
654
|
-
this
|
|
322
|
+
if( this.config.sessionIndex ) {
|
|
323
|
+
this.config.sessionIndexAttempts = 0;
|
|
655
324
|
}
|
|
656
325
|
this.#updateConfig();
|
|
657
|
-
|
|
658
|
-
return token;
|
|
326
|
+
return token;
|
|
659
327
|
})
|
|
660
|
-
.catch(
|
|
328
|
+
.catch(e => {
|
|
661
329
|
// eslint-disable-next-line no-console
|
|
662
|
-
console.
|
|
330
|
+
console.log(e)
|
|
663
331
|
});
|
|
664
332
|
}
|
|
665
333
|
|
|
666
334
|
/* eslint-disable camelcase */
|
|
667
335
|
async refreshToken(refresh_token) {
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
if (this.isNode && !this.crypto) {
|
|
671
|
-
// Deferring dynamic loading of node libraries til this first method to avoid doing this in constructor
|
|
672
|
-
await this.#importNodeLibs();
|
|
673
|
-
}
|
|
336
|
+
const {clientId, clientSecret, tokenUri} = this.config;
|
|
674
337
|
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
formData.append('client_secret', clientSecret);
|
|
683
|
-
}
|
|
684
|
-
formData.append('grant_type', 'refresh_token');
|
|
685
|
-
formData.append('refresh_token', refresh_token);
|
|
338
|
+
const formData = new URLSearchParams();
|
|
339
|
+
formData.append("client_id", clientId);
|
|
340
|
+
if( clientSecret ) {
|
|
341
|
+
formData.append("client_secret", clientSecret);
|
|
342
|
+
}
|
|
343
|
+
formData.append("grant_type", "refresh_token");
|
|
344
|
+
formData.append("refresh_token", refresh_token);
|
|
686
345
|
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
}),
|
|
346
|
+
return fetch(tokenUri, {
|
|
347
|
+
method: "POST",
|
|
348
|
+
headers: new Headers({
|
|
349
|
+
"content-type": "application/x-www-form-urlencoded",
|
|
350
|
+
}),
|
|
693
351
|
|
|
694
|
-
|
|
695
|
-
|
|
352
|
+
body: formData.toString(),
|
|
353
|
+
})
|
|
696
354
|
.then((response) => {
|
|
697
|
-
if
|
|
698
|
-
|
|
355
|
+
if( !response.ok && response.status === 401 ) {
|
|
356
|
+
return null;
|
|
699
357
|
}
|
|
700
358
|
return response.json();
|
|
701
359
|
})
|
|
702
|
-
.then(
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
360
|
+
.then(token => {
|
|
361
|
+
if( token ) {
|
|
362
|
+
// .expires_in contains the # of seconds before access token expires
|
|
363
|
+
// add property to keep track of current time when the token expires
|
|
364
|
+
token.eA = Date.now() + (token.expires_in * 1000);
|
|
365
|
+
}
|
|
366
|
+
return token;
|
|
709
367
|
})
|
|
710
|
-
.catch(
|
|
368
|
+
.catch(e => {
|
|
711
369
|
// eslint-disable-next-line no-console
|
|
712
|
-
console.
|
|
713
|
-
return null;
|
|
370
|
+
console.log(e)
|
|
714
371
|
});
|
|
715
372
|
}
|
|
716
373
|
|
|
717
374
|
async revokeTokens(access_token, refresh_token = null) {
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
if (this.isNode && !this.crypto) {
|
|
725
|
-
// Deferring dynamic loading of node libraries til this first method to avoid doing this in constructor
|
|
726
|
-
await this.#importNodeLibs();
|
|
727
|
-
}
|
|
375
|
+
if( !this.config || !this.config.revokeUri) {
|
|
376
|
+
// Must have a config structure and revokeUri to proceed
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
const {clientId, clientSecret, revokeUri} = this.config;
|
|
728
380
|
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
}
|
|
734
|
-
const aTknProps = ['access_token'];
|
|
735
|
-
if (refresh_token) {
|
|
736
|
-
aTknProps.push('refresh_token');
|
|
737
|
-
}
|
|
738
|
-
aTknProps.forEach((prop) => {
|
|
739
|
-
const formData = new URLSearchParams();
|
|
740
|
-
if (!clientSecret) {
|
|
741
|
-
formData.append('client_id', clientId);
|
|
381
|
+
const hdrs = {"content-type":"application/x-www-form-urlencoded"};
|
|
382
|
+
if( clientSecret ) {
|
|
383
|
+
const creds = `${clientId}:${clientSecret}`;
|
|
384
|
+
hdrs.authorization = `Basic ${window.btoa(creds)}`;
|
|
742
385
|
}
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
.then((response) => {
|
|
752
|
-
if (!response.ok) {
|
|
753
|
-
// eslint-disable-next-line no-console
|
|
754
|
-
console.error(`Error revoking ${prop}:${response.status}`);
|
|
386
|
+
const aTknProps = ["access_token"];
|
|
387
|
+
if( refresh_token ) {
|
|
388
|
+
aTknProps.push("refresh_token");
|
|
389
|
+
}
|
|
390
|
+
aTknProps.forEach( (prop) => {
|
|
391
|
+
const formData = new URLSearchParams();
|
|
392
|
+
if( !clientSecret ) {
|
|
393
|
+
formData.append("client_id", clientId);
|
|
755
394
|
}
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
395
|
+
formData.append("token", prop==="access_token" ? access_token : refresh_token);
|
|
396
|
+
formData.append("token_type_hint", prop);
|
|
397
|
+
fetch(revokeUri, {
|
|
398
|
+
method: "POST",
|
|
399
|
+
headers: new Headers(hdrs),
|
|
400
|
+
body: formData.toString(),
|
|
401
|
+
})
|
|
402
|
+
.then((response) => {
|
|
403
|
+
if( !response.ok ) {
|
|
404
|
+
// eslint-disable-next-line no-console
|
|
405
|
+
console.log( `Error revoking ${prop}:${response.status}` );
|
|
406
|
+
}
|
|
407
|
+
})
|
|
408
|
+
.catch(e => {
|
|
409
|
+
// eslint-disable-next-line no-console
|
|
410
|
+
console.log(e);
|
|
411
|
+
});
|
|
412
|
+
} );
|
|
413
|
+
// Also clobber any sessionIndex
|
|
414
|
+
if( this.config.sessionIndex ) {
|
|
415
|
+
delete this.config.sessionIndex;
|
|
416
|
+
this.#updateConfig();
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// For userinfo endpoint to return meaningful data, endpoint must include appAlias (if specified) and authorize must
|
|
421
|
+
// specify profile and optionally email scope to get such info returned
|
|
422
|
+
async getUserinfo(access_token) {
|
|
423
|
+
if( !this.config || !this.config.userinfoUri ) {
|
|
424
|
+
// Must have a config structure and userInfo to proceed
|
|
425
|
+
return {};
|
|
426
|
+
}
|
|
427
|
+
const hdrs = {'authorization':`bearer ${access_token}`,'content-type':'application/json;charset=UTF-8'};
|
|
428
|
+
return fetch(this.config.userinfoUri, {
|
|
429
|
+
method: "GET",
|
|
430
|
+
headers: new Headers(hdrs)})
|
|
431
|
+
.then( response => {
|
|
432
|
+
if( response.ok) {
|
|
433
|
+
return response.json();
|
|
434
|
+
} else {
|
|
435
|
+
// eslint-disable-next-line no-console
|
|
436
|
+
console.log( `Error invoking userinfo: ${response.status}` );
|
|
437
|
+
}
|
|
438
|
+
})
|
|
439
|
+
.then( data => {
|
|
440
|
+
return data;
|
|
441
|
+
})
|
|
442
|
+
.catch(e => {
|
|
443
|
+
// eslint-disable-next-line no-console
|
|
444
|
+
console.log(e);
|
|
761
445
|
});
|
|
762
|
-
this.#config.silentAuthFailed = false;
|
|
763
|
-
// Also clobber any sessionIndex
|
|
764
|
-
this.#updateSessionIndex(null);
|
|
765
446
|
}
|
|
447
|
+
|
|
766
448
|
/* eslint-enable camelcase */
|
|
767
449
|
|
|
450
|
+
// eslint-disable-next-line class-methods-use-this
|
|
768
451
|
#sha256Hash(str) {
|
|
769
|
-
|
|
770
|
-
// so using a different set of apis to get expected results.
|
|
771
|
-
if (this.isNode) {
|
|
772
|
-
return new Promise((resolve) => {
|
|
773
|
-
resolve(this.crypto.createHash('sha256').update(str).digest());
|
|
774
|
-
});
|
|
775
|
-
}
|
|
776
|
-
return this.subtle.digest('SHA-256', new TextEncoder().encode(str));
|
|
452
|
+
return window.crypto.subtle.digest("SHA-256", new TextEncoder().encode(str));
|
|
777
453
|
}
|
|
778
454
|
|
|
779
455
|
// Base64 encode
|
|
780
|
-
|
|
456
|
+
// eslint-disable-next-line class-methods-use-this
|
|
781
457
|
#encode64(buff) {
|
|
782
|
-
|
|
458
|
+
return window.btoa(new Uint8Array(buff).reduce((s, b) => s + String.fromCharCode(b), ''));
|
|
783
459
|
}
|
|
784
460
|
|
|
785
461
|
/*
|
|
786
462
|
* Base64 url safe encoding of an array
|
|
787
463
|
*/
|
|
788
464
|
#base64UrlSafeEncode(buf) {
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
/*
|
|
793
|
-
* Get Random string starting with buffer of specified size
|
|
794
|
-
*/
|
|
795
|
-
#getRandomString(nSize) {
|
|
796
|
-
const buf = new Uint8Array(nSize);
|
|
797
|
-
this.crypto.getRandomValues(buf);
|
|
798
|
-
return this.#base64UrlSafeEncode(buf);
|
|
465
|
+
const s = this.#encode64(buf).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
466
|
+
return s;
|
|
799
467
|
}
|
|
800
468
|
|
|
801
469
|
/* Calc code verifier if necessary
|
|
802
470
|
*/
|
|
803
471
|
/* eslint-disable camelcase */
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
.catch(
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
472
|
+
#getCodeChallenge(code_verifier) {
|
|
473
|
+
return this.#sha256Hash(code_verifier).then (
|
|
474
|
+
(hashed) => {
|
|
475
|
+
return this.#base64UrlSafeEncode(hashed)
|
|
476
|
+
}
|
|
477
|
+
).catch(
|
|
478
|
+
(error) => {
|
|
479
|
+
// eslint-disable-next-line no-console
|
|
480
|
+
console.log(error)
|
|
481
|
+
}
|
|
482
|
+
).finally(
|
|
483
|
+
() => { return null }
|
|
484
|
+
)
|
|
816
485
|
}
|
|
817
486
|
/* eslint-enable camelcase */
|
|
818
487
|
|
|
819
|
-
/*
|
|
820
|
-
* Return agent value for POST commands
|
|
821
|
-
*/
|
|
822
|
-
#getAgent() {
|
|
823
|
-
if (this.isNode && this.#config.ignoreInvalidCerts) {
|
|
824
|
-
const options = { rejectUnauthorized: false };
|
|
825
|
-
if (this.#config.legacyTLS) {
|
|
826
|
-
options.secureOptions = this.crypto.constants.SSL_OP_LEGACY_SERVER_CONNECT;
|
|
827
|
-
}
|
|
828
|
-
return new this.https.Agent(options);
|
|
829
|
-
}
|
|
830
|
-
return undefined;
|
|
831
|
-
}
|
|
832
488
|
}
|
|
833
489
|
|
|
834
490
|
export default PegaAuth;
|