@pega/custom-dx-components 8.8.8 → 8.8.10
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/package.json +2 -3
- package/src/auth.js +126 -91
- package/src/tasks/create/templates/Constellation/Field/Text-Phone/index.jsx.mustache +0 -1
- package/src/tasks/create/templates/Constellation/Template/DETAILS/demo.stories.jsx.mustache +20 -6
- package/src/tasks/create/templates/Constellation/Template/DETAILS/mock.stories.js.mustache +20 -10
- package/src/tasks/create/templates/Constellation/Template/DETAILS-region/demo.stories.jsx.mustache +20 -7
- package/src/tasks/create/templates/Constellation/Template/DETAILS-region/mock.stories.js.mustache +28 -13
- package/src/tasks/create/templates/Constellation/Template/FORM/demo.stories.jsx.mustache +90 -14
- package/src/tasks/create/templates/Constellation/Template/FORM/mock.stories.js.mustache +24 -12
- package/src/tasks/create/templates/Constellation/Template/FORM-region/demo.stories.jsx.mustache +89 -11
- package/src/tasks/create/templates/Constellation/Template/FORM-region/mock.stories.js.mustache +24 -12
- package/src/tasks/create/templates/Constellation/Template/PAGE/demo.stories.jsx.mustache +1 -0
- package/src/tasks/init/template/README.md +2 -204
- package/src/tasks/init/template/package.json.mustache +1 -1
- package/src/tasks/init/template/src/components/_components.md +1 -0
- package/src/tasks/init/template/tasks.config.json +5 -5
- package/src/tasks/init/template/src/components/Organization_Library_MediaURL/MediaURL.stories.tsx +0 -19
- package/src/tasks/init/template/src/components/Organization_Library_MediaURL/MediaURL.styles.ts +0 -5
- package/src/tasks/init/template/src/components/Organization_Library_MediaURL/MediaURL.types.ts +0 -10
- package/src/tasks/init/template/src/components/Organization_Library_MediaURL/MediaURLDisplay.tsx +0 -25
- package/src/tasks/init/template/src/components/Organization_Library_MediaURL/MediaURLInput.tsx +0 -24
- package/src/tasks/init/template/src/components/Organization_Library_MediaURL/config.json +0 -13
- package/src/tasks/init/template/src/components/Organization_Library_MediaURL/index.ts +0 -2
- package/src/tasks/init/template/src/components/Organization_Library_VideoPlayer/config.json +0 -44
- package/src/tasks/init/template/src/components/Organization_Library_VideoPlayer/demo.stories.jsx +0 -40
- package/src/tasks/init/template/src/components/Organization_Library_VideoPlayer/index.jsx +0 -42
- package/src/tasks/init/template/src/components/Organization_Library_VideoPlayer/styles.js +0 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pega/custom-dx-components",
|
|
3
|
-
"version": "8.8.
|
|
3
|
+
"version": "8.8.10",
|
|
4
4
|
"description": "Utility for building custom UI components",
|
|
5
5
|
"bin": "./bin/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -24,9 +24,8 @@
|
|
|
24
24
|
"babel-loader": "^8.2.5",
|
|
25
25
|
"chalk": "^5.0.1",
|
|
26
26
|
"compression-webpack-plugin": "^10.0.0",
|
|
27
|
-
"crypto": "^1.0.1",
|
|
28
27
|
"css-loader": "6.7.1",
|
|
29
|
-
"css-minimizer-webpack-plugin": "^
|
|
28
|
+
"css-minimizer-webpack-plugin": "^5.0.0",
|
|
30
29
|
"eslint": "^8.4.1",
|
|
31
30
|
"execa": "^6.1.0",
|
|
32
31
|
"file-loader": "6.2.0",
|
package/src/auth.js
CHANGED
|
@@ -1,14 +1,3 @@
|
|
|
1
|
-
// Below imports needed for when this file is used for a Node apps/Jest tests
|
|
2
|
-
// (can't do conditional imports...so should remove them for regular browser scenarios)
|
|
3
|
-
|
|
4
|
-
import fetch from 'node-fetch';
|
|
5
|
-
import open from 'open';
|
|
6
|
-
import crypto from 'node:crypto';
|
|
7
|
-
import https from 'node:https';
|
|
8
|
-
import http from 'node:http';
|
|
9
|
-
import fs from 'node:fs';
|
|
10
|
-
|
|
11
|
-
|
|
12
1
|
class PegaAuth {
|
|
13
2
|
constructor(ssKeyConfig) {
|
|
14
3
|
if (typeof ssKeyConfig === "string") {
|
|
@@ -18,16 +7,17 @@ class PegaAuth {
|
|
|
18
7
|
// object with config structure is passed in
|
|
19
8
|
this.config = ssKeyConfig;
|
|
20
9
|
}
|
|
10
|
+
this.urlencoded = 'application/x-www-form-urlencoded';
|
|
21
11
|
this.isNode = typeof window === 'undefined';
|
|
22
|
-
this.crypto = this.isNode ? crypto : window.crypto;
|
|
23
|
-
|
|
24
|
-
// Tried both preferring the subtle and the webcrypto but both still yileded different behavior on digest
|
|
25
12
|
|
|
26
|
-
//
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
13
|
+
// For isNode path the below attributes are initialized on first method invocation
|
|
14
|
+
if (!this.isNode) {
|
|
15
|
+
this.crypto = window.crypto;
|
|
16
|
+
this.subtle = window.crypto.subtle;
|
|
17
|
+
}
|
|
18
|
+
if (!this.config.serverType) {
|
|
19
|
+
this.config.serverType = 'infinity';
|
|
20
|
+
}
|
|
31
21
|
}
|
|
32
22
|
|
|
33
23
|
#reloadConfig() {
|
|
@@ -42,7 +32,7 @@ class PegaAuth {
|
|
|
42
32
|
} catch (e) {
|
|
43
33
|
try {
|
|
44
34
|
obj = JSON.parse(this.#atob(peConfig));
|
|
45
|
-
} catch (
|
|
35
|
+
} catch (err) {
|
|
46
36
|
obj = {};
|
|
47
37
|
}
|
|
48
38
|
}
|
|
@@ -56,10 +46,35 @@ class PegaAuth {
|
|
|
56
46
|
}
|
|
57
47
|
}
|
|
58
48
|
|
|
49
|
+
async #importSingleLib(libName, libProp) {
|
|
50
|
+
return import(libName).then((mod) => {
|
|
51
|
+
this[libProp] = mod.default;
|
|
52
|
+
}).catch((e) => {
|
|
53
|
+
console.error(`Library ${libName} failed to load`);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async #importNodeLibs() {
|
|
58
|
+
// Also current assumption is using Node 18 or better
|
|
59
|
+
return Promise.all([
|
|
60
|
+
this.#importSingleLib('node-fetch', 'fetch'),
|
|
61
|
+
this.#importSingleLib('open', 'open'),
|
|
62
|
+
this.#importSingleLib('node:crypto', 'crypto'),
|
|
63
|
+
this.#importSingleLib('node:https', 'https'),
|
|
64
|
+
this.#importSingleLib('node:http', 'http'),
|
|
65
|
+
this.#importSingleLib('node:fs', 'fs')
|
|
66
|
+
]).then(() => {
|
|
67
|
+
this.subtle = this.crypto.subtle || this.crypto.webcrypto.subtle;
|
|
68
|
+
/* eslint-disable-next-line no-global-assign */
|
|
69
|
+
fetch = this.fetch;
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
59
73
|
// For PKCE the authorize includes a code_challenge & code_challenge_method as well
|
|
60
74
|
async #buildAuthorizeUrl(state) {
|
|
61
75
|
const { serverType, clientId, redirectUri, authorizeUri, authService, sessionIndex, appAlias,
|
|
62
76
|
userIdentifier, password, noPKCE, isolationId } = this.config;
|
|
77
|
+
const bInfinity = serverType === 'infinity';
|
|
63
78
|
|
|
64
79
|
if (!noPKCE) {
|
|
65
80
|
// Generate random string of 64 chars for verifier. RFC 7636 says from 43-128 chars
|
|
@@ -83,63 +98,64 @@ class PegaAuth {
|
|
|
83
98
|
}
|
|
84
99
|
|
|
85
100
|
// Trim alias to include just the real alias piece
|
|
86
|
-
const addtlScope = appAlias ?
|
|
87
|
-
const scope = (
|
|
101
|
+
const addtlScope = appAlias ? `+app.alias.${appAlias.replace(/^app\//, '')}` : '';
|
|
102
|
+
const scope = (bInfinity ? `openid${addtlScope}` : 'user_info');
|
|
88
103
|
|
|
89
104
|
// Add explicit creds if specified to try to avoid login popup
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
: `&isolationID=${isolationId}
|
|
105
|
+
const authServiceArg = authService ? `&authentication_service=${encodeURIComponent(authService)}` : '';
|
|
106
|
+
const sessionIndexArg = sessionIndex && this.config.sessionIndexAttempts < 3 ? `&session_index=${sessionIndex}` : '';
|
|
107
|
+
const userIdentifierArg = userIdentifier ? `&UserIdentifier=${encodeURIComponent(userIdentifier)}` : '';
|
|
108
|
+
const passwordArg = password && userIdentifier ? `&Password=${encodeURIComponent(window.atob(password))}` : '';
|
|
109
|
+
const moreAuthArgs = bInfinity
|
|
110
|
+
? `&enable_psyncid=true${authServiceArg}${sessionIndexArg}${userIdentifierArg}${passwordArg}`
|
|
111
|
+
: `&isolationID=${isolationId}`;
|
|
97
112
|
|
|
98
113
|
let pkceArgs = '';
|
|
99
114
|
if (!noPKCE) {
|
|
100
115
|
const cc = await this.#getCodeChallenge(this.config.codeVerifier);
|
|
101
116
|
pkceArgs = `&code_challenge=${cc}&code_challenge_method=S256`;
|
|
102
117
|
}
|
|
103
|
-
|
|
104
|
-
// console.log(`authUrl: ${authUrl}`);
|
|
105
|
-
return authUrl;
|
|
118
|
+
return `${authorizeUri}?client_id=${clientId}&response_type=code&redirect_uri=${redirectUri}&scope=${scope}&state=${state}${pkceArgs}${moreAuthArgs}`;
|
|
106
119
|
}
|
|
107
120
|
|
|
108
121
|
async login() {
|
|
122
|
+
if (this.isNode && !this.crypto) {
|
|
123
|
+
// Deferring dynamic loading of node libraries til this first method to avoid doing this in constructor
|
|
124
|
+
await this.#importNodeLibs();
|
|
125
|
+
}
|
|
109
126
|
const { grantType } = this.config;
|
|
110
127
|
if (grantType && grantType !== 'authCode') {
|
|
111
128
|
return this.getToken();
|
|
112
|
-
} else {
|
|
113
|
-
return this.#authCodeStart();
|
|
114
129
|
}
|
|
130
|
+
return this.#authCodeStart();
|
|
115
131
|
}
|
|
116
132
|
|
|
117
133
|
// authCode login issues the authorize endpoint transaction and deals with redirects
|
|
134
|
+
/* eslint-disable-next-line sonarjs/cognitive-complexity */
|
|
118
135
|
async #authCodeStart() {
|
|
119
136
|
const fnGetRedirectUriOrigin = () => {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
137
|
+
const redirectUri = this.config.redirectUri;
|
|
138
|
+
const nRootOffset = redirectUri.indexOf("//");
|
|
139
|
+
const nFirstPathOffset = nRootOffset !== -1 ? redirectUri.indexOf("/", nRootOffset + 2) : -1;
|
|
123
140
|
return nFirstPathOffset !== -1 ? redirectUri.substring(0, nFirstPathOffset) : redirectUri;
|
|
124
141
|
};
|
|
125
142
|
|
|
126
143
|
const redirectOrigin = fnGetRedirectUriOrigin();
|
|
127
|
-
const state = this.isNode ? '' : this.#btoa(location.origin);
|
|
144
|
+
const state = this.isNode ? '' : this.#btoa(window.location.origin);
|
|
128
145
|
|
|
129
146
|
return new Promise((resolve, reject) => {
|
|
130
|
-
/* eslint-disable consistent-return */
|
|
131
147
|
this.#buildAuthorizeUrl(state).then((url) => {
|
|
132
148
|
let myWindow = null; // popup or iframe
|
|
133
|
-
let elIframe = null
|
|
134
|
-
|
|
149
|
+
let elIframe = null;
|
|
150
|
+
let elCloseBtn = null;
|
|
135
151
|
const iframeTimeout = this.config.silentTimeout !== undefined ? this.config.silentTimeout : 5000;
|
|
136
152
|
let bWinIframe = !this.isNode &&
|
|
137
153
|
iframeTimeout > 0 &&
|
|
138
154
|
((!!this.config.userIdentifier && !!this.config.password) ||
|
|
139
155
|
this.config.iframeLoginUI ||
|
|
140
156
|
this.config.authService !== 'pega');
|
|
141
|
-
let tmrAuthComplete = null
|
|
142
|
-
|
|
157
|
+
let tmrAuthComplete = null;
|
|
158
|
+
let checkWindowClosed = null;
|
|
143
159
|
const myWinOnLoad = () => {
|
|
144
160
|
try {
|
|
145
161
|
if (bWinIframe) {
|
|
@@ -152,8 +168,12 @@ class PegaAuth {
|
|
|
152
168
|
console.log("authjs(login): Exception trying to postMessage on load");
|
|
153
169
|
}
|
|
154
170
|
};
|
|
171
|
+
/* eslint-disable prefer-promise-reject-errors */
|
|
155
172
|
const fnOpenPopup = () => {
|
|
156
|
-
|
|
173
|
+
if (this.config.noPopups) {
|
|
174
|
+
return reject("no-popups");
|
|
175
|
+
}
|
|
176
|
+
myWindow = (this.isNode ? this.open : window.open)(url, '_blank', 'width=700,height=500,left=200,top=100');
|
|
157
177
|
if (!myWindow) {
|
|
158
178
|
// Blocked by popup-blocker
|
|
159
179
|
return reject("blocked");
|
|
@@ -172,14 +192,17 @@ class PegaAuth {
|
|
|
172
192
|
}
|
|
173
193
|
}
|
|
174
194
|
};
|
|
195
|
+
/* eslint-enable prefer-promise-reject-errors */
|
|
175
196
|
const fnCloseIframe = () => {
|
|
176
197
|
elIframe.parentNode.removeChild(elIframe);
|
|
177
198
|
elCloseBtn.parentNode.removeChild(elCloseBtn);
|
|
178
|
-
elIframe =
|
|
199
|
+
elIframe = null;
|
|
200
|
+
elCloseBtn = null;
|
|
179
201
|
bWinIframe = false;
|
|
180
202
|
};
|
|
181
203
|
const fnCloseAndReject = () => {
|
|
182
204
|
fnCloseIframe();
|
|
205
|
+
/* eslint-disable-next-line prefer-promise-reject-errors */
|
|
183
206
|
reject("closed");
|
|
184
207
|
};
|
|
185
208
|
let authMessageReceiver = null;
|
|
@@ -192,11 +215,9 @@ class PegaAuth {
|
|
|
192
215
|
if (bWinIframe) {
|
|
193
216
|
clearTimeout(tmrAuthComplete);
|
|
194
217
|
fnCloseIframe();
|
|
195
|
-
} else {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
myWindow.close();
|
|
199
|
-
}
|
|
218
|
+
} else if (!this.isNode) {
|
|
219
|
+
clearInterval(checkWindowClosed);
|
|
220
|
+
myWindow.close();
|
|
200
221
|
}
|
|
201
222
|
resolve(token);
|
|
202
223
|
})
|
|
@@ -209,9 +230,9 @@ class PegaAuth {
|
|
|
209
230
|
if (bWinIframe) {
|
|
210
231
|
const nFrameZLevel = 99999;
|
|
211
232
|
elIframe = document.createElement('iframe');
|
|
212
|
-
elIframe.id =
|
|
213
|
-
const loginBoxWidth = 500
|
|
214
|
-
|
|
233
|
+
elIframe.id = `pe${this.config.clientId}`;
|
|
234
|
+
const loginBoxWidth = 500;
|
|
235
|
+
const loginBoxHeight = 700;
|
|
215
236
|
const oStyle = elIframe.style;
|
|
216
237
|
oStyle.position = 'absolute';
|
|
217
238
|
oStyle.display = 'none';
|
|
@@ -226,7 +247,8 @@ class PegaAuth {
|
|
|
226
247
|
elIframe.addEventListener('load', myWinOnLoad, true);
|
|
227
248
|
// Disallow iframe content attempts to navigate main window
|
|
228
249
|
elIframe.setAttribute('sandbox', 'allow-scripts allow-forms allow-same-origin');
|
|
229
|
-
|
|
250
|
+
// Adding prompt=none as this is standard OIDC way to communicate no UI is expected (expecting Pega security to support this one day)
|
|
251
|
+
elIframe.setAttribute('src', `${url}&prompt=none`);
|
|
230
252
|
|
|
231
253
|
const svgCloseBtn = `<?xml version="1.0" encoding="UTF-8"?>
|
|
232
254
|
<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">
|
|
@@ -283,20 +305,19 @@ class PegaAuth {
|
|
|
283
305
|
const isHttp = redirectUri.startsWith('http:');
|
|
284
306
|
const nLocalhost = redirectUri.indexOf("localhost:");
|
|
285
307
|
const nSlash = redirectUri.indexOf('/', nLocalhost + 10);
|
|
286
|
-
const nPort = parseInt(redirectUri.substring(nLocalhost + 10, nSlash));
|
|
287
|
-
if (
|
|
308
|
+
const nPort = parseInt(redirectUri.substring(nLocalhost + 10, nSlash), 10);
|
|
309
|
+
if (nLocalhost !== -1) {
|
|
288
310
|
const options = key && cert && !isHttp ? {
|
|
289
|
-
key: fs.readFileSync(key),
|
|
290
|
-
cert: fs.readFileSync(cert)
|
|
311
|
+
key: this.fs.readFileSync(key),
|
|
312
|
+
cert: this.fs.readFileSync(cert)
|
|
291
313
|
} : {};
|
|
292
|
-
const server = (isHttp ? http : https).createServer(options, (req, res) => {
|
|
293
|
-
const { method, url } = req;
|
|
314
|
+
const server = (isHttp ? this.http : this.https).createServer(options, (req, res) => {
|
|
294
315
|
const { winTitle, winBodyHtml } = this.config;
|
|
295
|
-
// console.log(`HTTP request received: ${url}`);
|
|
316
|
+
// console.log(`HTTP request received: ${req.url}`);
|
|
296
317
|
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
297
318
|
// Auto closing window for now. Can always leave it up and allow authConfig props to set title and bodyHtml
|
|
298
319
|
res.end(`<html><head><title>${winTitle}</title><script>window.close();</script></head><body>${winBodyHtml}</body></html>`);
|
|
299
|
-
const queryString = url.split('?')[1];
|
|
320
|
+
const queryString = req.url.split('?')[1];
|
|
300
321
|
const urlParams = new URLSearchParams(queryString);
|
|
301
322
|
const code = urlParams.get('code');
|
|
302
323
|
if (code) {
|
|
@@ -306,6 +327,7 @@ class PegaAuth {
|
|
|
306
327
|
fnGetTokenAndFinish(code);
|
|
307
328
|
}
|
|
308
329
|
});
|
|
330
|
+
/* eslint-enable no-undef */
|
|
309
331
|
server.listen(nPort);
|
|
310
332
|
}
|
|
311
333
|
}
|
|
@@ -323,7 +345,7 @@ class PegaAuth {
|
|
|
323
345
|
fnGetTokenAndFinish(code);
|
|
324
346
|
};
|
|
325
347
|
window.addEventListener('message', authMessageReceiver, false);
|
|
326
|
-
window.authCodeCallback =
|
|
348
|
+
window.authCodeCallback = (code) => {
|
|
327
349
|
console.log('authjs(login): authCodeCallback used with code');
|
|
328
350
|
fnGetTokenAndFinish(code);
|
|
329
351
|
};
|
|
@@ -333,12 +355,13 @@ class PegaAuth {
|
|
|
333
355
|
}
|
|
334
356
|
|
|
335
357
|
// For PKCE token endpoint includes code_verifier
|
|
358
|
+
/* eslint-disable-next-line sonarjs/cognitive-complexity */
|
|
336
359
|
getToken(authCode) {
|
|
337
360
|
// Reload config to pick up the previously stored codeVerifier
|
|
338
361
|
this.#reloadConfig();
|
|
339
362
|
|
|
340
363
|
const { serverType, isolationId, clientId, clientSecret, redirectUri, tokenUri, codeVerifier, grantType,
|
|
341
|
-
sessionIndex, customTokenParams, userIdentifier, password, noPKCE
|
|
364
|
+
sessionIndex, customTokenParams, userIdentifier, password, noPKCE } = this.config;
|
|
342
365
|
|
|
343
366
|
const bAuthCode = !grantType || grantType === 'authCode';
|
|
344
367
|
if (bAuthCode && !authCode && !this.isNode) {
|
|
@@ -352,14 +375,14 @@ class PegaAuth {
|
|
|
352
375
|
if (clientSecret) {
|
|
353
376
|
formData.append('client_secret', clientSecret);
|
|
354
377
|
}
|
|
378
|
+
/* eslint-disable camelcase */
|
|
355
379
|
const fullGTName = {
|
|
356
380
|
"authCode": "authorization_code",
|
|
357
381
|
"clientCreds": "client_credentials",
|
|
358
382
|
"customBearer": "custom-bearer",
|
|
359
383
|
"passwordCreds": "password"
|
|
360
384
|
}[grantType];
|
|
361
|
-
const grant_type = fullGTName
|
|
362
|
-
(grantType ? grantType : 'authorization_code');
|
|
385
|
+
const grant_type = fullGTName || grantType || 'authorization_code';
|
|
363
386
|
formData.append('grant_type', grant_type);
|
|
364
387
|
if (serverType === 'launchpad' && grantType !== 'authCode') {
|
|
365
388
|
formData.append('isolation_ids', isolationId);
|
|
@@ -373,6 +396,7 @@ class PegaAuth {
|
|
|
373
396
|
} else if (sessionIndex) {
|
|
374
397
|
formData.append('session_index', sessionIndex);
|
|
375
398
|
}
|
|
399
|
+
/* eslint-enable camelcase */
|
|
376
400
|
if (grantType === 'customBearer' && customTokenParams) {
|
|
377
401
|
Object.keys(customTokenParams).forEach((param) => {
|
|
378
402
|
formData.append(param, customTokenParams[param]);
|
|
@@ -382,13 +406,12 @@ class PegaAuth {
|
|
|
382
406
|
formData.append('username', userIdentifier);
|
|
383
407
|
formData.append('password', this.#atob(password));
|
|
384
408
|
}
|
|
385
|
-
// console.log(`getToken form data: ${formData.toString()}`);
|
|
386
409
|
|
|
387
410
|
return fetch(tokenUri, {
|
|
388
|
-
agent: this
|
|
411
|
+
agent: this.#getAgent(),
|
|
389
412
|
method: 'POST',
|
|
390
413
|
headers: new Headers({
|
|
391
|
-
'content-type':
|
|
414
|
+
'content-type': this.urlencoded
|
|
392
415
|
}),
|
|
393
416
|
|
|
394
417
|
body: formData.toString()
|
|
@@ -397,11 +420,10 @@ class PegaAuth {
|
|
|
397
420
|
.then((token) => {
|
|
398
421
|
if (token.errors || token.error) {
|
|
399
422
|
// console.log(`formData: ${formData.toString()}`);
|
|
400
|
-
|
|
423
|
+
console.log(`Token endpoint error: ${JSON.stringify(token.errors || token.error)}`);
|
|
401
424
|
} else {
|
|
402
425
|
// .expires_in contains the # of seconds before access token expires
|
|
403
426
|
// add property to keep track of current time when the token expires
|
|
404
|
-
/* eslint-disable camelcase */
|
|
405
427
|
token.eA = Date.now() + token.expires_in * 1000;
|
|
406
428
|
if (this.config.codeVerifier) {
|
|
407
429
|
delete this.config.codeVerifier;
|
|
@@ -417,7 +439,6 @@ class PegaAuth {
|
|
|
417
439
|
this.#updateConfig();
|
|
418
440
|
}
|
|
419
441
|
return token;
|
|
420
|
-
/* eslint-enable camelcase */
|
|
421
442
|
})
|
|
422
443
|
.catch((e) => {
|
|
423
444
|
console.log(e);
|
|
@@ -426,7 +447,12 @@ class PegaAuth {
|
|
|
426
447
|
|
|
427
448
|
/* eslint-disable camelcase */
|
|
428
449
|
async refreshToken(refresh_token) {
|
|
429
|
-
const { clientId, clientSecret, tokenUri
|
|
450
|
+
const { clientId, clientSecret, tokenUri } = this.config;
|
|
451
|
+
|
|
452
|
+
if (this.isNode && !this.crypto) {
|
|
453
|
+
// Deferring dynamic loading of node libraries til this first method to avoid doing this in constructor
|
|
454
|
+
await this.#importNodeLibs();
|
|
455
|
+
}
|
|
430
456
|
|
|
431
457
|
const formData = new URLSearchParams();
|
|
432
458
|
formData.append('client_id', clientId);
|
|
@@ -437,16 +463,16 @@ class PegaAuth {
|
|
|
437
463
|
formData.append('refresh_token', refresh_token);
|
|
438
464
|
|
|
439
465
|
return fetch(tokenUri, {
|
|
440
|
-
agent: this
|
|
466
|
+
agent: this.#getAgent(),
|
|
441
467
|
method: 'POST',
|
|
442
468
|
headers: new Headers({
|
|
443
|
-
'content-type':
|
|
469
|
+
'content-type': this.urlencoded
|
|
444
470
|
}),
|
|
445
471
|
|
|
446
472
|
body: formData.toString()
|
|
447
473
|
})
|
|
448
474
|
.then((response) => {
|
|
449
|
-
if (!response.ok &&
|
|
475
|
+
if (!response.ok && response.status === 401) {
|
|
450
476
|
return null;
|
|
451
477
|
}
|
|
452
478
|
return response.json();
|
|
@@ -469,13 +495,19 @@ class PegaAuth {
|
|
|
469
495
|
// Must have a config structure to proceed
|
|
470
496
|
return;
|
|
471
497
|
}
|
|
472
|
-
const { clientId, clientSecret, revokeUri
|
|
498
|
+
const { clientId, clientSecret, revokeUri } = this.config;
|
|
499
|
+
|
|
500
|
+
if (this.isNode && !this.crypto) {
|
|
501
|
+
// Deferring dynamic loading of node libraries til this first method to avoid doing this in constructor
|
|
502
|
+
await this.#importNodeLibs();
|
|
503
|
+
}
|
|
473
504
|
|
|
474
|
-
|
|
505
|
+
const hdrs = { 'content-type': this.urlencoded };
|
|
475
506
|
if (clientSecret) {
|
|
476
|
-
|
|
507
|
+
const basicCreds = window.btoa(`${clientId}:${clientSecret}`);
|
|
508
|
+
hdrs.authorization = `Basic ${basicCreds}`;
|
|
477
509
|
}
|
|
478
|
-
|
|
510
|
+
const aTknProps = ['access_token'];
|
|
479
511
|
if (refresh_token) {
|
|
480
512
|
aTknProps.push('refresh_token');
|
|
481
513
|
}
|
|
@@ -487,14 +519,14 @@ class PegaAuth {
|
|
|
487
519
|
formData.append('token', prop === 'access_token' ? access_token : refresh_token);
|
|
488
520
|
formData.append('token_type_hint', prop);
|
|
489
521
|
fetch(revokeUri, {
|
|
490
|
-
agent: this
|
|
522
|
+
agent: this.#getAgent(),
|
|
491
523
|
method: 'POST',
|
|
492
524
|
headers: new Headers(hdrs),
|
|
493
525
|
body: formData.toString()
|
|
494
526
|
})
|
|
495
527
|
.then((response) => {
|
|
496
528
|
if (!response.ok) {
|
|
497
|
-
console.log(
|
|
529
|
+
console.log(`Error revoking ${prop}:${response.status}`);
|
|
498
530
|
}
|
|
499
531
|
})
|
|
500
532
|
.catch((e) => {
|
|
@@ -516,31 +548,27 @@ class PegaAuth {
|
|
|
516
548
|
return new Promise((resolve) => {
|
|
517
549
|
return resolve(this.crypto.createHash("sha256").update(str).digest());
|
|
518
550
|
});
|
|
519
|
-
} else {
|
|
520
|
-
return this.subtle.digest('SHA-256', new TextEncoder().encode(str));
|
|
521
551
|
}
|
|
552
|
+
return this.subtle.digest('SHA-256', new TextEncoder().encode(str));
|
|
522
553
|
}
|
|
523
554
|
|
|
524
|
-
/* btoa to work with node or browser - create B64 string*/
|
|
555
|
+
/* btoa to work with node or browser - create B64 string */
|
|
525
556
|
#btoa(str) {
|
|
526
557
|
if (this.isNode) {
|
|
527
558
|
return Buffer.from(str, 'binary').toString('base64');
|
|
528
|
-
} else {
|
|
529
|
-
return window.btoa(str);
|
|
530
559
|
}
|
|
560
|
+
return window.btoa(str);
|
|
531
561
|
}
|
|
532
562
|
|
|
533
563
|
/* atoa to work with node or browser - convert B64 to binary */
|
|
534
564
|
#atob(str) {
|
|
535
565
|
if (this.isNode) {
|
|
536
566
|
return Buffer.from(str, 'base64').toString('binary');
|
|
537
|
-
} else {
|
|
538
|
-
return window.atob(str);
|
|
539
567
|
}
|
|
568
|
+
return window.atob(str);
|
|
540
569
|
}
|
|
541
570
|
|
|
542
571
|
// Base64 encode
|
|
543
|
-
/* eslint-disable-next-line class-methods-use-this */
|
|
544
572
|
#encode64(buff) {
|
|
545
573
|
return this.#btoa(new Uint8Array(buff).reduce((s, b) => s + String.fromCharCode(b), ''));
|
|
546
574
|
}
|
|
@@ -555,7 +583,7 @@ class PegaAuth {
|
|
|
555
583
|
/* Calc code verifier if necessary
|
|
556
584
|
*/
|
|
557
585
|
/* eslint-disable camelcase */
|
|
558
|
-
#getCodeChallenge(code_verifier) {
|
|
586
|
+
async #getCodeChallenge(code_verifier) {
|
|
559
587
|
return this.#sha256Hash(code_verifier)
|
|
560
588
|
.then((hashed) => {
|
|
561
589
|
return this.#base64UrlSafeEncode(hashed);
|
|
@@ -568,6 +596,13 @@ class PegaAuth {
|
|
|
568
596
|
});
|
|
569
597
|
}
|
|
570
598
|
/* eslint-enable camelcase */
|
|
599
|
+
|
|
600
|
+
/*
|
|
601
|
+
* Return agent value for POST commands
|
|
602
|
+
*/
|
|
603
|
+
#getAgent() {
|
|
604
|
+
return this.isNode && this.config.ignoreInvalidCerts ? new this.https.Agent({ rejectUnauthorized: false }) : undefined;
|
|
605
|
+
}
|
|
571
606
|
}
|
|
572
607
|
|
|
573
608
|
export default PegaAuth;
|
|
@@ -14,7 +14,6 @@ import handleEvent from "./event-utils";
|
|
|
14
14
|
import { suggestionsHandler } from './suggestions-handler';
|
|
15
15
|
|
|
16
16
|
import Styled{{COMPONENT_CLASS_NAME}}Wrapper from './styles';
|
|
17
|
-
import { } from "@pega/cosmos-react-core";
|
|
18
17
|
|
|
19
18
|
export const formatExists = (formatterVal) => {
|
|
20
19
|
const formatterValues = [
|
|
@@ -14,6 +14,7 @@ const renderField = resolvedProps => {
|
|
|
14
14
|
displayMode,
|
|
15
15
|
value = '',
|
|
16
16
|
label = '',
|
|
17
|
+
key,
|
|
17
18
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
18
19
|
theme = useTheme()
|
|
19
20
|
} = resolvedProps;
|
|
@@ -22,7 +23,7 @@ const renderField = resolvedProps => {
|
|
|
22
23
|
|
|
23
24
|
let val =
|
|
24
25
|
value !== '' ? (
|
|
25
|
-
<Text variant='h1' as='span'>
|
|
26
|
+
<Text variant='h1' as='span' key={key}>
|
|
26
27
|
{value}
|
|
27
28
|
</Text>
|
|
28
29
|
) : (
|
|
@@ -30,13 +31,22 @@ const renderField = resolvedProps => {
|
|
|
30
31
|
);
|
|
31
32
|
|
|
32
33
|
if (label === 'Create date/time')
|
|
33
|
-
val =
|
|
34
|
+
val = (
|
|
35
|
+
<DateTimeDisplay
|
|
36
|
+
value={value}
|
|
37
|
+
variant='datetime'
|
|
38
|
+
format='long'
|
|
39
|
+
clockFormat={null}
|
|
40
|
+
key={key}
|
|
41
|
+
/>
|
|
42
|
+
);
|
|
34
43
|
|
|
35
|
-
if (displayAsStatus === true) val = <StatusWorkRenderer value={value} />;
|
|
44
|
+
if (displayAsStatus === true) val = <StatusWorkRenderer value={value} key={key} />;
|
|
36
45
|
|
|
37
46
|
if (label === 'Create Operator')
|
|
38
47
|
val = (
|
|
39
48
|
<Button
|
|
49
|
+
key={key}
|
|
40
50
|
variant='link'
|
|
41
51
|
style={
|
|
42
52
|
label !== null
|
|
@@ -49,15 +59,19 @@ const renderField = resolvedProps => {
|
|
|
49
59
|
);
|
|
50
60
|
|
|
51
61
|
if (variant === 'inline') {
|
|
52
|
-
val = value ||
|
|
62
|
+
val = value || (
|
|
63
|
+
<span aria-hidden='true' key={key}>
|
|
64
|
+
––
|
|
65
|
+
</span>
|
|
66
|
+
);
|
|
53
67
|
} else {
|
|
54
68
|
val = (
|
|
55
|
-
<Text variant='h1' as='span'>
|
|
69
|
+
<Text variant='h1' as='span' key={key}>
|
|
56
70
|
{val}
|
|
57
71
|
</Text>
|
|
58
72
|
);
|
|
59
73
|
}
|
|
60
|
-
return <FieldValueList variant={variant} fields={[{ name: label, value: val }]} />;
|
|
74
|
+
return <FieldValueList variant={variant} fields={[{ name: label, value: val }]} key={key} />;
|
|
61
75
|
};
|
|
62
76
|
|
|
63
77
|
export const base{{COMPONENT_CLASS_NAME}} = () => {
|