@pega/custom-dx-components 0.23.18 → 0.23.19
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 +3 -3
- package/src/tasks/authenticate/index.js +54 -15
- package/src/tasks/bundle/builder/webpack.config.js +12 -0
- package/src/tasks/create/templates/Constellation/Field/Boolean/index.jsx.mustache +1 -1
- package/src/tasks/create/templates/Constellation/Field/Decimal-Currency/index.jsx.mustache +1 -2
- package/src/tasks/init/template/package.json.mustache +2 -2
- package/tests/assets/components/createAll/Constellation/Pega_DXIL_MyBoolean/index.jsx +1 -1
- package/tests/assets/components/createAll/Constellation/Pega_DXIL_MyCurrency/index.jsx +1 -2
- package/tests/assets/components/createAll/Launchpad/Pega_DXIL_MyBoolean/index.jsx +1 -1
- package/tests/assets/components/createAll/Launchpad/Pega_DXIL_MyCurrency/index.jsx +1 -2
- package/src/auth.js +0 -637
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pega/custom-dx-components",
|
|
3
|
-
"version": "0.23.
|
|
3
|
+
"version": "0.23.19",
|
|
4
4
|
"description": "Utility for building custom UI components",
|
|
5
5
|
"bin": "./bin/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"@babel/core": "^7.23.3",
|
|
19
19
|
"@babel/plugin-transform-class-properties": "^7.23.3",
|
|
20
20
|
"@babel/preset-react": "^7.23.3",
|
|
21
|
+
"@pega/auth": "^0.1.1",
|
|
21
22
|
"adm-zip": "^0.5.9",
|
|
22
23
|
"ajv": "^8.12.0",
|
|
23
24
|
"ajv-keywords": "^5.1.0",
|
|
@@ -39,7 +40,6 @@
|
|
|
39
40
|
"mustache": "^4.2.0",
|
|
40
41
|
"ncp": "^2.0.0",
|
|
41
42
|
"node-fetch": "^3.2.10",
|
|
42
|
-
"open": "^8.4.2",
|
|
43
43
|
"ora": "^6.1.2",
|
|
44
44
|
"pascalcase": "^2.0.0",
|
|
45
45
|
"postcss": "^8.4.16",
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
],
|
|
66
66
|
"devDependencies": {
|
|
67
67
|
"@babel/preset-env": "^7.23.3",
|
|
68
|
-
"@pega/configs": "^0.
|
|
68
|
+
"@pega/configs": "^0.5.3",
|
|
69
69
|
"@pega/prettier-config": "^0.5.3",
|
|
70
70
|
"@types/jest": "^29.5.3",
|
|
71
71
|
"babel-jest": "^29.6.1",
|
|
@@ -5,8 +5,14 @@ import chalk from 'chalk';
|
|
|
5
5
|
import inquirer from 'inquirer';
|
|
6
6
|
|
|
7
7
|
import { checkPathAccess, showVersion } from '../../util.js';
|
|
8
|
-
import {
|
|
9
|
-
|
|
8
|
+
import {
|
|
9
|
+
TOKEN_PATH,
|
|
10
|
+
INF_TOKEN_URL_V1,
|
|
11
|
+
INF_AUTHORIZE_URL_V1,
|
|
12
|
+
LP_TOKEN_URL,
|
|
13
|
+
LP_AUTHORIZE_URL
|
|
14
|
+
} from '../../constants.js';
|
|
15
|
+
import { PegaAuth } from '@pega/auth';
|
|
10
16
|
|
|
11
17
|
import { getAuthQuestions } from './helper.js';
|
|
12
18
|
|
|
@@ -15,15 +21,27 @@ export const TASKS_CONFIG_JSON_FILENAME = 'tasks.config.json';
|
|
|
15
21
|
const pegaConfigJsonPath = path.join(path.resolve(), TASKS_CONFIG_JSON_FILENAME);
|
|
16
22
|
const server = null;
|
|
17
23
|
|
|
18
|
-
const generateAccessToken = async
|
|
24
|
+
const generateAccessToken = async config => {
|
|
19
25
|
let authSuccess = false;
|
|
20
|
-
const {
|
|
26
|
+
const {
|
|
27
|
+
serverType,
|
|
28
|
+
authService,
|
|
29
|
+
isolationId,
|
|
30
|
+
grantType,
|
|
31
|
+
clientId,
|
|
32
|
+
clientSecret,
|
|
33
|
+
user,
|
|
34
|
+
password,
|
|
35
|
+
legacyTLS
|
|
36
|
+
} = config;
|
|
21
37
|
// Try to gracefully deal with missing trailing slashes for server (and later conditionally for redirectUri)
|
|
22
38
|
const server = config.server.endsWith('/') ? config.server : `${config.server}/`;
|
|
23
39
|
|
|
24
40
|
return new Promise((resolve, reject) => {
|
|
25
41
|
const sTokenUri = `${server}${serverType === 'infinity' ? INF_TOKEN_URL_V1 : LP_TOKEN_URL}`;
|
|
26
|
-
const sAuthorizeUri = `${server}${
|
|
42
|
+
const sAuthorizeUri = `${server}${
|
|
43
|
+
serverType === 'infinity' ? INF_AUTHORIZE_URL_V1 : LP_AUTHORIZE_URL
|
|
44
|
+
}`;
|
|
27
45
|
// console.log(`grantType: ${config.grantType}`);
|
|
28
46
|
const browserAuthSuccessMarkup = `<div style="width: 300px; padding: 10px;">
|
|
29
47
|
<svg xmlns="http://www. w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 311 68" version="1.1">
|
|
@@ -53,7 +71,9 @@ const generateAccessToken = async (config) => {
|
|
|
53
71
|
grantType === 'authCode'
|
|
54
72
|
? {
|
|
55
73
|
authorizeUri: sAuthorizeUri,
|
|
56
|
-
redirectUri: config.redirectUri.endsWith('/')
|
|
74
|
+
redirectUri: config.redirectUri.endsWith('/')
|
|
75
|
+
? config.redirectUri
|
|
76
|
+
: `${config.redirectUri}/`,
|
|
57
77
|
cert: config.cert,
|
|
58
78
|
key: config.key,
|
|
59
79
|
winTitle: 'DX Component Dev',
|
|
@@ -90,16 +110,18 @@ const generateAccessToken = async (config) => {
|
|
|
90
110
|
const auth = new PegaAuth(authConfig);
|
|
91
111
|
if (grantType === 'authCode') {
|
|
92
112
|
console.log(
|
|
93
|
-
`${chalk.green(
|
|
113
|
+
`${chalk.green(
|
|
114
|
+
'Awaiting authorization to complete (Ctrl-C to exit). You may need to enter credentials within your default browser.'
|
|
115
|
+
)}`
|
|
94
116
|
);
|
|
95
117
|
}
|
|
96
118
|
// console.log(`authConfig: ${JSON.stringify(authConfig)}`);
|
|
97
119
|
|
|
98
120
|
auth
|
|
99
121
|
.login()
|
|
100
|
-
.then(
|
|
122
|
+
.then(token => {
|
|
101
123
|
if (token?.token_type) {
|
|
102
|
-
fs.writeFile(TOKEN_PATH, JSON.stringify(token),
|
|
124
|
+
fs.writeFile(TOKEN_PATH, JSON.stringify(token), err => {
|
|
103
125
|
if (err) {
|
|
104
126
|
console.error(err);
|
|
105
127
|
return reject(authSuccess);
|
|
@@ -109,11 +131,15 @@ const generateAccessToken = async (config) => {
|
|
|
109
131
|
return resolve(authSuccess);
|
|
110
132
|
});
|
|
111
133
|
} else {
|
|
112
|
-
console.log(
|
|
134
|
+
console.log(
|
|
135
|
+
`${chalk.bold.red(
|
|
136
|
+
`Authentication failed:${JSON.stringify(token.errors ? token.errors : token)}`
|
|
137
|
+
)}`
|
|
138
|
+
);
|
|
113
139
|
return reject(authSuccess);
|
|
114
140
|
}
|
|
115
141
|
})
|
|
116
|
-
.catch(
|
|
142
|
+
.catch(e => {
|
|
117
143
|
console.log(`${chalk.bold.red(`Authentication failed:${e}`)}`);
|
|
118
144
|
return reject(authSuccess);
|
|
119
145
|
});
|
|
@@ -124,7 +150,7 @@ const generateAccessToken = async (config) => {
|
|
|
124
150
|
});
|
|
125
151
|
};
|
|
126
152
|
|
|
127
|
-
export default async
|
|
153
|
+
export default async options => {
|
|
128
154
|
let authOK = false;
|
|
129
155
|
|
|
130
156
|
showVersion();
|
|
@@ -151,7 +177,18 @@ export default async (options) => {
|
|
|
151
177
|
const answers = await inquirer.prompt(questions);
|
|
152
178
|
const aAsked = answers.aAsked;
|
|
153
179
|
delete answers.aAsked;
|
|
154
|
-
const aProps = [
|
|
180
|
+
const aProps = [
|
|
181
|
+
'serverType',
|
|
182
|
+
'server',
|
|
183
|
+
'isolationId',
|
|
184
|
+
'clientId',
|
|
185
|
+
'grantType',
|
|
186
|
+
'authService',
|
|
187
|
+
'redirectUri',
|
|
188
|
+
'clientSecret',
|
|
189
|
+
'user',
|
|
190
|
+
'password'
|
|
191
|
+
];
|
|
155
192
|
const isLaunchPad = answers.serverType === 'launchpad';
|
|
156
193
|
let bWriteUpdate = false;
|
|
157
194
|
for (let i = 0; i < aProps.length; i += 1) {
|
|
@@ -169,7 +206,9 @@ export default async (options) => {
|
|
|
169
206
|
}
|
|
170
207
|
// Update the askedOnce array based on askOnce criteria
|
|
171
208
|
const aAskOnce = serverConfig.questions_askOnce ? serverConfig.questions_askOnce.split(',') : [];
|
|
172
|
-
const aAskedOnce = serverConfig.questions_askedOnce
|
|
209
|
+
const aAskedOnce = serverConfig.questions_askedOnce
|
|
210
|
+
? serverConfig.questions_askedOnce.split(',')
|
|
211
|
+
: [];
|
|
173
212
|
let bAskedOnceUpdated = false;
|
|
174
213
|
for (let j = 0; j < aAsked.length; j += 1) {
|
|
175
214
|
const prop = aAsked[j];
|
|
@@ -202,7 +241,7 @@ export default async (options) => {
|
|
|
202
241
|
try {
|
|
203
242
|
updCfg = JSON.stringify(data, null, 2);
|
|
204
243
|
// console.log(`updated config: ${updCfg}`);
|
|
205
|
-
fs.writeFileSync(pegaConfigJsonPath, updCfg,
|
|
244
|
+
fs.writeFileSync(pegaConfigJsonPath, updCfg, err => {
|
|
206
245
|
if (err) throw err;
|
|
207
246
|
});
|
|
208
247
|
} catch (e) {}
|
|
@@ -316,6 +316,18 @@ export default (
|
|
|
316
316
|
contextRegExp: /@pega\\cosmos-react-core\\lib\\components\\Icon$/
|
|
317
317
|
}),
|
|
318
318
|
|
|
319
|
+
/* Ignore cosmos streamline-icons */
|
|
320
|
+
new webpack.IgnorePlugin({
|
|
321
|
+
resourceRegExp: /^\.\/streamline-icons$/,
|
|
322
|
+
contextRegExp: /@pega\/cosmos-react-core\/lib\/components\/Icon$/
|
|
323
|
+
}),
|
|
324
|
+
|
|
325
|
+
/* Ignore cosmos streamline-icons - windows */
|
|
326
|
+
new webpack.IgnorePlugin({
|
|
327
|
+
resourceRegExp: /^\.\/streamline-icons$/,
|
|
328
|
+
contextRegExp: /@pega\\cosmos-react-core\\lib\\components\\Icon$/
|
|
329
|
+
}),
|
|
330
|
+
|
|
319
331
|
new webpack.optimize.LimitChunkCountPlugin({
|
|
320
332
|
maxChunks: 1
|
|
321
333
|
}),
|
|
@@ -263,8 +263,7 @@ const {{COMPONENT_CLASS_NAME}} = props => {
|
|
|
263
263
|
currencyDecimalPrecision: 'auto',
|
|
264
264
|
isTableFormatter: false,
|
|
265
265
|
alwaysShowISOCode: false,
|
|
266
|
-
hasSuggestions: false
|
|
267
|
-
fieldMetadata: {}
|
|
266
|
+
hasSuggestions: false
|
|
268
267
|
};
|
|
269
268
|
|
|
270
269
|
{{COMPONENT_CLASS_NAME}}.propTypes = {
|
|
@@ -66,8 +66,8 @@
|
|
|
66
66
|
},
|
|
67
67
|
"devDependencies": {
|
|
68
68
|
"@pega/configs": "^0.5.3",
|
|
69
|
-
"@pega/custom-dx-components": "^0.23.
|
|
70
|
-
"@pega/eslint-config": "^0.3
|
|
69
|
+
"@pega/custom-dx-components": "^0.23.19",
|
|
70
|
+
"@pega/eslint-config": "^0.5.3",
|
|
71
71
|
"@storybook/addon-actions": "^6.5.12",
|
|
72
72
|
"@storybook/addon-essentials": "^6.5.12",
|
|
73
73
|
"@storybook/addon-links": "^6.5.12",
|
|
@@ -260,8 +260,7 @@ PegaDxilMyCurrency.defaultProps = {
|
|
|
260
260
|
currencyDecimalPrecision: 'auto',
|
|
261
261
|
isTableFormatter: false,
|
|
262
262
|
alwaysShowISOCode: false,
|
|
263
|
-
hasSuggestions: false
|
|
264
|
-
fieldMetadata: {}
|
|
263
|
+
hasSuggestions: false
|
|
265
264
|
};
|
|
266
265
|
|
|
267
266
|
PegaDxilMyCurrency.propTypes = {
|
|
@@ -260,8 +260,7 @@ PegaDxilMyCurrency.defaultProps = {
|
|
|
260
260
|
currencyDecimalPrecision: 'auto',
|
|
261
261
|
isTableFormatter: false,
|
|
262
262
|
alwaysShowISOCode: false,
|
|
263
|
-
hasSuggestions: false
|
|
264
|
-
fieldMetadata: {}
|
|
263
|
+
hasSuggestions: false
|
|
265
264
|
};
|
|
266
265
|
|
|
267
266
|
PegaDxilMyCurrency.propTypes = {
|
package/src/auth.js
DELETED
|
@@ -1,637 +0,0 @@
|
|
|
1
|
-
class PegaAuth {
|
|
2
|
-
constructor(ssKeyConfig) {
|
|
3
|
-
if (typeof ssKeyConfig === 'string') {
|
|
4
|
-
this.ssKeyConfig = ssKeyConfig;
|
|
5
|
-
this.#reloadConfig();
|
|
6
|
-
} else {
|
|
7
|
-
// object with config structure is passed in
|
|
8
|
-
this.config = ssKeyConfig;
|
|
9
|
-
}
|
|
10
|
-
this.urlencoded = 'application/x-www-form-urlencoded';
|
|
11
|
-
this.isNode = typeof window === 'undefined';
|
|
12
|
-
|
|
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
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
#reloadConfig() {
|
|
24
|
-
if (!this.ssKeyConfig) {
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
const peConfig = window.sessionStorage.getItem(this.ssKeyConfig);
|
|
28
|
-
let obj = {};
|
|
29
|
-
if (peConfig) {
|
|
30
|
-
try {
|
|
31
|
-
obj = JSON.parse(peConfig);
|
|
32
|
-
} catch (e) {
|
|
33
|
-
try {
|
|
34
|
-
obj = JSON.parse(atob(peConfig));
|
|
35
|
-
} catch (err) {
|
|
36
|
-
obj = {};
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
this.config = peConfig ? obj : {};
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
#updateConfig() {
|
|
44
|
-
if (this.ssKeyConfig) {
|
|
45
|
-
window.sessionStorage.setItem(this.ssKeyConfig, btoa(JSON.stringify(this.config)));
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
async #importSingleLib(libName, libProp) {
|
|
50
|
-
return import(libName)
|
|
51
|
-
.then((mod) => {
|
|
52
|
-
this[libProp] = mod.default;
|
|
53
|
-
})
|
|
54
|
-
.catch((e) => {
|
|
55
|
-
console.error(`Library ${libName} failed to load`);
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
async #importNodeLibs() {
|
|
60
|
-
// Also current assumption is using Node 18 or better
|
|
61
|
-
return Promise.all([
|
|
62
|
-
this.#importSingleLib('node-fetch', 'fetch'),
|
|
63
|
-
this.#importSingleLib('open', 'open'),
|
|
64
|
-
this.#importSingleLib('node:crypto', 'crypto'),
|
|
65
|
-
this.#importSingleLib('node:https', 'https'),
|
|
66
|
-
this.#importSingleLib('node:http', 'http'),
|
|
67
|
-
this.#importSingleLib('node:fs', 'fs')
|
|
68
|
-
]).then(() => {
|
|
69
|
-
this.subtle = this.crypto.subtle || this.crypto.webcrypto.subtle;
|
|
70
|
-
/* eslint-disable-next-line no-global-assign */
|
|
71
|
-
fetch = this.fetch;
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// For PKCE the authorize includes a code_challenge & code_challenge_method as well
|
|
76
|
-
async #buildAuthorizeUrl(state) {
|
|
77
|
-
const {
|
|
78
|
-
serverType,
|
|
79
|
-
clientId,
|
|
80
|
-
redirectUri,
|
|
81
|
-
authorizeUri,
|
|
82
|
-
authService,
|
|
83
|
-
sessionIndex,
|
|
84
|
-
appAlias,
|
|
85
|
-
userIdentifier,
|
|
86
|
-
password,
|
|
87
|
-
noPKCE,
|
|
88
|
-
isolationId
|
|
89
|
-
} = this.config;
|
|
90
|
-
const bInfinity = serverType === 'infinity';
|
|
91
|
-
|
|
92
|
-
if (!noPKCE) {
|
|
93
|
-
// Generate random string of 64 chars for verifier. RFC 7636 says from 43-128 chars
|
|
94
|
-
const buf = new Uint8Array(64);
|
|
95
|
-
this.crypto.getRandomValues(buf);
|
|
96
|
-
this.config.codeVerifier = this.#base64UrlSafeEncode(buf);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// If sessionIndex exists then increment attempts count (we will stop sending session_index after two failures)
|
|
100
|
-
if (sessionIndex) {
|
|
101
|
-
this.config.sessionIndexAttempts += 1;
|
|
102
|
-
}
|
|
103
|
-
// Persist codeVerifier in session storage so it survives the redirects that are to follow
|
|
104
|
-
this.#updateConfig();
|
|
105
|
-
|
|
106
|
-
if (!state) {
|
|
107
|
-
// Calc random state variable
|
|
108
|
-
const buf = new Uint8Array(32);
|
|
109
|
-
this.crypto.getRandomValues(buf);
|
|
110
|
-
state = this.#base64UrlSafeEncode(buf);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// Trim alias to include just the real alias piece
|
|
114
|
-
const addtlScope = appAlias ? `+app.alias.${appAlias.replace(/^app\//, '')}` : '';
|
|
115
|
-
const scope = bInfinity ? `openid${addtlScope}` : 'user_info';
|
|
116
|
-
|
|
117
|
-
// Add explicit creds if specified to try to avoid login popup
|
|
118
|
-
const authServiceArg = authService ? `&authentication_service=${encodeURIComponent(authService)}` : '';
|
|
119
|
-
const sessionIndexArg =
|
|
120
|
-
sessionIndex && this.config.sessionIndexAttempts < 3 ? `&session_index=${sessionIndex}` : '';
|
|
121
|
-
const userIdentifierArg = userIdentifier ? `&UserIdentifier=${encodeURIComponent(userIdentifier)}` : '';
|
|
122
|
-
const passwordArg = password && userIdentifier ? `&Password=${encodeURIComponent(atob(password))}` : '';
|
|
123
|
-
const moreAuthArgs = bInfinity
|
|
124
|
-
? `&enable_psyncid=true${authServiceArg}${sessionIndexArg}${userIdentifierArg}${passwordArg}`
|
|
125
|
-
: `&isolationID=${isolationId}`;
|
|
126
|
-
|
|
127
|
-
let pkceArgs = '';
|
|
128
|
-
if (!noPKCE) {
|
|
129
|
-
const cc = await this.#getCodeChallenge(this.config.codeVerifier);
|
|
130
|
-
pkceArgs = `&code_challenge=${cc}&code_challenge_method=S256`;
|
|
131
|
-
}
|
|
132
|
-
return `${authorizeUri}?client_id=${clientId}&response_type=code&redirect_uri=${redirectUri}&scope=${scope}&state=${state}${pkceArgs}${moreAuthArgs}`;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
async login() {
|
|
136
|
-
if (this.isNode && !this.crypto) {
|
|
137
|
-
// Deferring dynamic loading of node libraries til this first method to avoid doing this in constructor
|
|
138
|
-
await this.#importNodeLibs();
|
|
139
|
-
}
|
|
140
|
-
const { grantType, noPKCE } = this.config;
|
|
141
|
-
if (grantType && grantType !== 'authCode') {
|
|
142
|
-
return this.getToken();
|
|
143
|
-
}
|
|
144
|
-
// Make sure browser in a secure context, else PKCE will fail
|
|
145
|
-
if (!this.isNode && !noPKCE && !window.isSecureContext) {
|
|
146
|
-
throw (new Error(`Authorization code grant flow failed due to insecure browser context at ${window.location.origin}. Use localhost or https.`));
|
|
147
|
-
}
|
|
148
|
-
return this.#authCodeStart();
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// authCode login issues the authorize endpoint transaction and deals with redirects
|
|
152
|
-
/* eslint-disable-next-line sonarjs/cognitive-complexity */
|
|
153
|
-
async #authCodeStart() {
|
|
154
|
-
const fnGetRedirectUriOrigin = () => {
|
|
155
|
-
const redirectUri = this.config.redirectUri;
|
|
156
|
-
const nRootOffset = redirectUri.indexOf('//');
|
|
157
|
-
const nFirstPathOffset = nRootOffset !== -1 ? redirectUri.indexOf('/', nRootOffset + 2) : -1;
|
|
158
|
-
return nFirstPathOffset !== -1 ? redirectUri.substring(0, nFirstPathOffset) : redirectUri;
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
const redirectOrigin = fnGetRedirectUriOrigin();
|
|
162
|
-
const state = this.isNode ? '' : btoa(window.location.origin);
|
|
163
|
-
|
|
164
|
-
return new Promise((resolve, reject) => {
|
|
165
|
-
this.#buildAuthorizeUrl(state).then((url) => {
|
|
166
|
-
let myWindow = null; // popup or iframe
|
|
167
|
-
let elIframe = null;
|
|
168
|
-
let elCloseBtn = null;
|
|
169
|
-
const iframeTimeout = this.config.silentTimeout !== undefined ? this.config.silentTimeout : 5000;
|
|
170
|
-
let bWinIframe =
|
|
171
|
-
!this.isNode &&
|
|
172
|
-
iframeTimeout > 0 &&
|
|
173
|
-
((!!this.config.userIdentifier && !!this.config.password) ||
|
|
174
|
-
this.config.iframeLoginUI ||
|
|
175
|
-
this.config.authService !== 'pega');
|
|
176
|
-
let tmrAuthComplete = null;
|
|
177
|
-
let checkWindowClosed = null;
|
|
178
|
-
const myWinOnLoad = () => {
|
|
179
|
-
try {
|
|
180
|
-
if (bWinIframe) {
|
|
181
|
-
elIframe.contentWindow.postMessage({ type: 'PegaAuth' }, redirectOrigin);
|
|
182
|
-
} else {
|
|
183
|
-
myWindow.postMessage({ type: 'PegaAuth' }, redirectOrigin);
|
|
184
|
-
}
|
|
185
|
-
} catch (e) {
|
|
186
|
-
// Exception trying to postMessage on load (perhaps should console.warn)
|
|
187
|
-
}
|
|
188
|
-
};
|
|
189
|
-
/* eslint-disable prefer-promise-reject-errors */
|
|
190
|
-
const fnOpenPopup = () => {
|
|
191
|
-
if (this.config.noPopups) {
|
|
192
|
-
return reject('no-popups');
|
|
193
|
-
}
|
|
194
|
-
myWindow = (this.isNode ? this.open : window.open)(url, '_blank', 'width=700,height=500,left=200,top=100');
|
|
195
|
-
if (!myWindow) {
|
|
196
|
-
// Blocked by popup-blocker
|
|
197
|
-
return reject('blocked');
|
|
198
|
-
}
|
|
199
|
-
checkWindowClosed = setInterval(() => {
|
|
200
|
-
if (myWindow.closed) {
|
|
201
|
-
clearInterval(checkWindowClosed);
|
|
202
|
-
reject('closed');
|
|
203
|
-
}
|
|
204
|
-
}, 500);
|
|
205
|
-
if (!this.isNode) {
|
|
206
|
-
try {
|
|
207
|
-
myWindow.addEventListener('load', myWinOnLoad, true);
|
|
208
|
-
} catch (e) {
|
|
209
|
-
// Exception trying to add onload handler to opened window
|
|
210
|
-
console.error(`Error adding event listener on popup window: ${e}`);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
};
|
|
214
|
-
/* eslint-enable prefer-promise-reject-errors */
|
|
215
|
-
const fnCloseIframe = () => {
|
|
216
|
-
elIframe.parentNode.removeChild(elIframe);
|
|
217
|
-
elCloseBtn.parentNode.removeChild(elCloseBtn);
|
|
218
|
-
elIframe = null;
|
|
219
|
-
elCloseBtn = null;
|
|
220
|
-
bWinIframe = false;
|
|
221
|
-
};
|
|
222
|
-
const fnCloseAndReject = () => {
|
|
223
|
-
fnCloseIframe();
|
|
224
|
-
/* eslint-disable-next-line prefer-promise-reject-errors */
|
|
225
|
-
reject('closed');
|
|
226
|
-
};
|
|
227
|
-
let authMessageReceiver = null;
|
|
228
|
-
/* Retrieve token(s) and close login window */
|
|
229
|
-
const fnGetTokenAndFinish = (code) => {
|
|
230
|
-
if (!this.isNode) {
|
|
231
|
-
window.removeEventListener('message', authMessageReceiver, false);
|
|
232
|
-
}
|
|
233
|
-
this.getToken(code)
|
|
234
|
-
.then((token) => {
|
|
235
|
-
if (bWinIframe) {
|
|
236
|
-
clearTimeout(tmrAuthComplete);
|
|
237
|
-
fnCloseIframe();
|
|
238
|
-
} else if (!this.isNode) {
|
|
239
|
-
clearInterval(checkWindowClosed);
|
|
240
|
-
myWindow.close();
|
|
241
|
-
}
|
|
242
|
-
resolve(token);
|
|
243
|
-
})
|
|
244
|
-
.catch((e) => {
|
|
245
|
-
reject(e);
|
|
246
|
-
});
|
|
247
|
-
};
|
|
248
|
-
// If there is a userIdentifier and password specified or an external SSO auth service,
|
|
249
|
-
// we can try to use this silently in an iFrame first
|
|
250
|
-
if (bWinIframe) {
|
|
251
|
-
const nFrameZLevel = 99999;
|
|
252
|
-
elIframe = document.createElement('iframe');
|
|
253
|
-
elIframe.id = `pe${this.config.clientId}`;
|
|
254
|
-
const loginBoxWidth = 500;
|
|
255
|
-
const loginBoxHeight = 700;
|
|
256
|
-
const oStyle = elIframe.style;
|
|
257
|
-
oStyle.position = 'absolute';
|
|
258
|
-
oStyle.display = 'none';
|
|
259
|
-
oStyle.zIndex = nFrameZLevel;
|
|
260
|
-
oStyle.top = `${Math.round(Math.max(window.innerHeight - loginBoxHeight, 0) / 2)}px`;
|
|
261
|
-
oStyle.left = `${Math.round(Math.max(window.innerWidth - loginBoxWidth, 0) / 2)}px`;
|
|
262
|
-
oStyle.width = '500px';
|
|
263
|
-
oStyle.height = '700px';
|
|
264
|
-
// Add Iframe to top of document DOM to have it load
|
|
265
|
-
document.body.insertBefore(elIframe, document.body.firstChild);
|
|
266
|
-
// document.getElementsByTagName('body')[0].appendChild(elIframe);
|
|
267
|
-
elIframe.addEventListener('load', myWinOnLoad, true);
|
|
268
|
-
// Disallow iframe content attempts to navigate main window
|
|
269
|
-
elIframe.setAttribute('sandbox', 'allow-scripts allow-forms allow-same-origin');
|
|
270
|
-
// Adding prompt=none as this is standard OIDC way to communicate no UI is expected (expecting Pega security to support this one day)
|
|
271
|
-
elIframe.setAttribute('src', `${url}&prompt=none`);
|
|
272
|
-
|
|
273
|
-
const svgCloseBtn = `<?xml version="1.0" encoding="UTF-8"?>
|
|
274
|
-
<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">
|
|
275
|
-
<title>Dismiss - Black</title>
|
|
276
|
-
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
|
277
|
-
<g transform="translate(1.000000, 1.000000)">
|
|
278
|
-
<circle fill="#252C32" cx="16" cy="16" r="16"></circle>
|
|
279
|
-
<g transform="translate(9.109375, 9.214844)" fill="#FFFFFF" fill-rule="nonzero">
|
|
280
|
-
<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>
|
|
281
|
-
</g>
|
|
282
|
-
</g>
|
|
283
|
-
</g>
|
|
284
|
-
</svg>`;
|
|
285
|
-
const bCloseWithinFrame = false;
|
|
286
|
-
elCloseBtn = document.createElement('img');
|
|
287
|
-
elCloseBtn.onclick = fnCloseAndReject;
|
|
288
|
-
elCloseBtn.src = `data:image/svg+xml;base64,${btoa(svgCloseBtn)}`;
|
|
289
|
-
const oBtnStyle = elCloseBtn.style;
|
|
290
|
-
oBtnStyle.cursor = 'pointer';
|
|
291
|
-
// If svg doesn't set width and height might want to set oBtStyle width and height to something like '2em'
|
|
292
|
-
oBtnStyle.position = 'absolute';
|
|
293
|
-
oBtnStyle.display = 'none';
|
|
294
|
-
oBtnStyle.zIndex = nFrameZLevel + 1;
|
|
295
|
-
const nTopOffset = bCloseWithinFrame ? 5 : -10;
|
|
296
|
-
const nRightOffset = bCloseWithinFrame ? -34 : -20;
|
|
297
|
-
const nTop = Math.round(Math.max(window.innerHeight - loginBoxHeight, 0) / 2) + nTopOffset;
|
|
298
|
-
oBtnStyle.top = `${nTop}px`;
|
|
299
|
-
const nLeft = Math.round(Math.max(window.innerWidth - loginBoxWidth, 0) / 2) + loginBoxWidth + nRightOffset;
|
|
300
|
-
oBtnStyle.left = `${nLeft}px`;
|
|
301
|
-
document.body.insertBefore(elCloseBtn, document.body.firstChild);
|
|
302
|
-
// If the password was wrong, then the login screen will be in the iframe
|
|
303
|
-
// ..and with Pega without realization of US-372314 it may replace the top (main portal) window
|
|
304
|
-
// For now set a timer and if the timer expires, remove the iFrame and use same url within
|
|
305
|
-
// visible window
|
|
306
|
-
tmrAuthComplete = setTimeout(() => {
|
|
307
|
-
clearTimeout(tmrAuthComplete);
|
|
308
|
-
// remove password from config
|
|
309
|
-
if (this.config.password) {
|
|
310
|
-
delete this.config.password;
|
|
311
|
-
this.#updateConfig();
|
|
312
|
-
}
|
|
313
|
-
// Display the iframe where the redirects did not succeed (or invoke a popup window)
|
|
314
|
-
if (this.config.iframeLoginUI) {
|
|
315
|
-
elIframe.style.display = 'block';
|
|
316
|
-
elCloseBtn.style.display = 'block';
|
|
317
|
-
} else {
|
|
318
|
-
fnCloseIframe();
|
|
319
|
-
fnOpenPopup();
|
|
320
|
-
}
|
|
321
|
-
}, iframeTimeout);
|
|
322
|
-
} else {
|
|
323
|
-
if (this.isNode) {
|
|
324
|
-
// Determine port to listen to by extracting it from redirect uri
|
|
325
|
-
const { redirectUri, cert, key } = this.config;
|
|
326
|
-
const isHttp = redirectUri.startsWith('http:');
|
|
327
|
-
const nLocalhost = redirectUri.indexOf('localhost:');
|
|
328
|
-
const nSlash = redirectUri.indexOf('/', nLocalhost + 10);
|
|
329
|
-
const nPort = parseInt(redirectUri.substring(nLocalhost + 10, nSlash), 10);
|
|
330
|
-
if (nLocalhost !== -1) {
|
|
331
|
-
const options =
|
|
332
|
-
key && cert && !isHttp
|
|
333
|
-
? {
|
|
334
|
-
key: this.fs.readFileSync(key),
|
|
335
|
-
cert: this.fs.readFileSync(cert)
|
|
336
|
-
}
|
|
337
|
-
: {};
|
|
338
|
-
const server = (isHttp ? this.http : this.https).createServer(options, (req, res) => {
|
|
339
|
-
const { winTitle, winBodyHtml } = this.config;
|
|
340
|
-
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
341
|
-
// Auto closing window for now. Can always leave it up and allow authConfig props to set title and bodyHtml
|
|
342
|
-
res.end(
|
|
343
|
-
`<html><head><title>${winTitle}</title><script>window.close();</script></head><body>${winBodyHtml}</body></html>`
|
|
344
|
-
);
|
|
345
|
-
const queryString = req.url.split('?')[1];
|
|
346
|
-
const urlParams = new URLSearchParams(queryString);
|
|
347
|
-
const code = urlParams.get('code');
|
|
348
|
-
if (code) {
|
|
349
|
-
// Stop receiving connections and close when all are handled.
|
|
350
|
-
server.close();
|
|
351
|
-
fnGetTokenAndFinish(code);
|
|
352
|
-
}
|
|
353
|
-
});
|
|
354
|
-
/* eslint-enable no-undef */
|
|
355
|
-
server.listen(nPort);
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
fnOpenPopup();
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
if (!this.isNode) {
|
|
362
|
-
/* Handler to receive the auth code */
|
|
363
|
-
authMessageReceiver = (event) => {
|
|
364
|
-
// Check origin to make sure it is the redirect origin
|
|
365
|
-
if (event.origin !== redirectOrigin) return;
|
|
366
|
-
if (!event.data || !event.data.type || event.data.type !== 'PegaAuth') return;
|
|
367
|
-
const code = event.data.code.toString();
|
|
368
|
-
fnGetTokenAndFinish(code);
|
|
369
|
-
};
|
|
370
|
-
window.addEventListener('message', authMessageReceiver, false);
|
|
371
|
-
window.authCodeCallback = (code) => {
|
|
372
|
-
fnGetTokenAndFinish(code);
|
|
373
|
-
};
|
|
374
|
-
}
|
|
375
|
-
});
|
|
376
|
-
});
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
// For PKCE token endpoint includes code_verifier
|
|
380
|
-
/* eslint-disable-next-line sonarjs/cognitive-complexity */
|
|
381
|
-
getToken(authCode) {
|
|
382
|
-
// Reload config to pick up the previously stored codeVerifier
|
|
383
|
-
this.#reloadConfig();
|
|
384
|
-
|
|
385
|
-
const {
|
|
386
|
-
serverType,
|
|
387
|
-
isolationId,
|
|
388
|
-
clientId,
|
|
389
|
-
clientSecret,
|
|
390
|
-
redirectUri,
|
|
391
|
-
tokenUri,
|
|
392
|
-
codeVerifier,
|
|
393
|
-
grantType,
|
|
394
|
-
sessionIndex,
|
|
395
|
-
customTokenParams,
|
|
396
|
-
userIdentifier,
|
|
397
|
-
password,
|
|
398
|
-
noPKCE
|
|
399
|
-
} = this.config;
|
|
400
|
-
|
|
401
|
-
const bAuthCode = !grantType || grantType === 'authCode';
|
|
402
|
-
if (bAuthCode && !authCode && !this.isNode) {
|
|
403
|
-
const queryString = window.location.search;
|
|
404
|
-
const urlParams = new URLSearchParams(queryString);
|
|
405
|
-
authCode = urlParams.get('code');
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
const formData = new URLSearchParams();
|
|
409
|
-
formData.append('client_id', clientId);
|
|
410
|
-
if (clientSecret) {
|
|
411
|
-
formData.append('client_secret', clientSecret);
|
|
412
|
-
}
|
|
413
|
-
/* eslint-disable camelcase */
|
|
414
|
-
const fullGTName = {
|
|
415
|
-
authCode: 'authorization_code',
|
|
416
|
-
clientCreds: 'client_credentials',
|
|
417
|
-
customBearer: 'custom-bearer',
|
|
418
|
-
passwordCreds: 'password'
|
|
419
|
-
}[grantType];
|
|
420
|
-
const grant_type = fullGTName || grantType || 'authorization_code';
|
|
421
|
-
formData.append('grant_type', grant_type);
|
|
422
|
-
if (serverType === 'launchpad' && grantType !== 'authCode') {
|
|
423
|
-
formData.append('isolation_ids', isolationId);
|
|
424
|
-
}
|
|
425
|
-
if (bAuthCode) {
|
|
426
|
-
formData.append('code', authCode);
|
|
427
|
-
formData.append('redirect_uri', redirectUri);
|
|
428
|
-
if (!noPKCE) {
|
|
429
|
-
formData.append('code_verifier', codeVerifier);
|
|
430
|
-
}
|
|
431
|
-
} else if (sessionIndex) {
|
|
432
|
-
formData.append('session_index', sessionIndex);
|
|
433
|
-
}
|
|
434
|
-
/* eslint-enable camelcase */
|
|
435
|
-
if (grantType === 'customBearer' && customTokenParams) {
|
|
436
|
-
Object.keys(customTokenParams).forEach((param) => {
|
|
437
|
-
formData.append(param, customTokenParams[param]);
|
|
438
|
-
});
|
|
439
|
-
}
|
|
440
|
-
if (grantType !== 'authCode') {
|
|
441
|
-
formData.append('enable_psyncId', 'true');
|
|
442
|
-
}
|
|
443
|
-
if (grantType === 'passwordCreds') {
|
|
444
|
-
formData.append('username', userIdentifier);
|
|
445
|
-
formData.append('password', atob(password));
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
return fetch(tokenUri, {
|
|
449
|
-
agent: this.#getAgent(),
|
|
450
|
-
method: 'POST',
|
|
451
|
-
headers: new Headers({
|
|
452
|
-
'content-type': this.urlencoded
|
|
453
|
-
}),
|
|
454
|
-
|
|
455
|
-
body: formData.toString()
|
|
456
|
-
})
|
|
457
|
-
.then((response) => response.json())
|
|
458
|
-
.then((token) => {
|
|
459
|
-
if (token.errors || token.error) {
|
|
460
|
-
console.error(`Token endpoint error: ${JSON.stringify(token.errors || token.error)}`);
|
|
461
|
-
} else {
|
|
462
|
-
// .expires_in contains the # of seconds before access token expires
|
|
463
|
-
// add property to keep track of current time when the token expires
|
|
464
|
-
token.eA = Date.now() + token.expires_in * 1000;
|
|
465
|
-
if (this.config.codeVerifier) {
|
|
466
|
-
delete this.config.codeVerifier;
|
|
467
|
-
}
|
|
468
|
-
// If there is a session_index then move this to the peConfig structure (as used on authorize)
|
|
469
|
-
if (token.session_index) {
|
|
470
|
-
this.config.sessionIndex = token.session_index;
|
|
471
|
-
}
|
|
472
|
-
// If we got a token and have a session index, then reset the sessionIndexAttempts
|
|
473
|
-
if (this.config.sessionIndex) {
|
|
474
|
-
this.config.sessionIndexAttempts = 0;
|
|
475
|
-
}
|
|
476
|
-
this.#updateConfig();
|
|
477
|
-
}
|
|
478
|
-
return token;
|
|
479
|
-
})
|
|
480
|
-
.catch((e) => {
|
|
481
|
-
console.error(`Token endpoint error: ${e}`);
|
|
482
|
-
});
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
/* eslint-disable camelcase */
|
|
486
|
-
async refreshToken(refresh_token) {
|
|
487
|
-
const { clientId, clientSecret, tokenUri } = this.config;
|
|
488
|
-
|
|
489
|
-
if (this.isNode && !this.crypto) {
|
|
490
|
-
// Deferring dynamic loading of node libraries til this first method to avoid doing this in constructor
|
|
491
|
-
await this.#importNodeLibs();
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
const formData = new URLSearchParams();
|
|
495
|
-
formData.append('client_id', clientId);
|
|
496
|
-
if (clientSecret) {
|
|
497
|
-
formData.append('client_secret', clientSecret);
|
|
498
|
-
}
|
|
499
|
-
formData.append('grant_type', 'refresh_token');
|
|
500
|
-
formData.append('refresh_token', refresh_token);
|
|
501
|
-
|
|
502
|
-
return fetch(tokenUri, {
|
|
503
|
-
agent: this.#getAgent(),
|
|
504
|
-
method: 'POST',
|
|
505
|
-
headers: new Headers({
|
|
506
|
-
'content-type': this.urlencoded
|
|
507
|
-
}),
|
|
508
|
-
|
|
509
|
-
body: formData.toString()
|
|
510
|
-
})
|
|
511
|
-
.then((response) => {
|
|
512
|
-
if (!response.ok && response.status === 401) {
|
|
513
|
-
return null;
|
|
514
|
-
}
|
|
515
|
-
return response.json();
|
|
516
|
-
})
|
|
517
|
-
.then((token) => {
|
|
518
|
-
if (token) {
|
|
519
|
-
// .expires_in contains the # of seconds before access token expires
|
|
520
|
-
// add property to keep track of current time when the token expires
|
|
521
|
-
token.eA = Date.now() + token.expires_in * 1000;
|
|
522
|
-
}
|
|
523
|
-
return token;
|
|
524
|
-
})
|
|
525
|
-
.catch((e) => {
|
|
526
|
-
console.warn(`Refresh token failed: ${e}`);
|
|
527
|
-
});
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
async revokeTokens(access_token, refresh_token = null) {
|
|
531
|
-
if (Object.keys(this.config).length === 0) {
|
|
532
|
-
// Must have a config structure to proceed
|
|
533
|
-
return;
|
|
534
|
-
}
|
|
535
|
-
const { clientId, clientSecret, revokeUri } = this.config;
|
|
536
|
-
|
|
537
|
-
if (this.isNode && !this.crypto) {
|
|
538
|
-
// Deferring dynamic loading of node libraries til this first method to avoid doing this in constructor
|
|
539
|
-
await this.#importNodeLibs();
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
const hdrs = { 'content-type': this.urlencoded };
|
|
543
|
-
if (clientSecret) {
|
|
544
|
-
const basicCreds = btoa(`${clientId}:${clientSecret}`);
|
|
545
|
-
hdrs.authorization = `Basic ${basicCreds}`;
|
|
546
|
-
}
|
|
547
|
-
const aTknProps = ['access_token'];
|
|
548
|
-
if (refresh_token) {
|
|
549
|
-
aTknProps.push('refresh_token');
|
|
550
|
-
}
|
|
551
|
-
aTknProps.forEach((prop) => {
|
|
552
|
-
const formData = new URLSearchParams();
|
|
553
|
-
if (!clientSecret) {
|
|
554
|
-
formData.append('client_id', clientId);
|
|
555
|
-
}
|
|
556
|
-
formData.append('token', prop === 'access_token' ? access_token : refresh_token);
|
|
557
|
-
formData.append('token_type_hint', prop);
|
|
558
|
-
fetch(revokeUri, {
|
|
559
|
-
agent: this.#getAgent(),
|
|
560
|
-
method: 'POST',
|
|
561
|
-
headers: new Headers(hdrs),
|
|
562
|
-
body: formData.toString()
|
|
563
|
-
})
|
|
564
|
-
.then((response) => {
|
|
565
|
-
if (!response.ok) {
|
|
566
|
-
console.error(`Error revoking ${prop}:${response.status}`);
|
|
567
|
-
}
|
|
568
|
-
})
|
|
569
|
-
.catch((e) => {
|
|
570
|
-
console.error(`Error revoking ${prop}`);
|
|
571
|
-
});
|
|
572
|
-
});
|
|
573
|
-
// Also clobber any sessionIndex
|
|
574
|
-
if (this.config.sessionIndex) {
|
|
575
|
-
delete this.config.sessionIndex;
|
|
576
|
-
this.#updateConfig();
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
/* eslint-enable camelcase */
|
|
580
|
-
|
|
581
|
-
#sha256Hash(str) {
|
|
582
|
-
// Found that the Node implementation of subtle.digest is yielding incorrect results
|
|
583
|
-
// so using a different set of apis to get expected results.
|
|
584
|
-
if (this.isNode) {
|
|
585
|
-
return new Promise((resolve) => {
|
|
586
|
-
return resolve(this.crypto.createHash('sha256').update(str).digest());
|
|
587
|
-
});
|
|
588
|
-
}
|
|
589
|
-
return this.subtle.digest('SHA-256', new TextEncoder().encode(str));
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
// Base64 encode
|
|
593
|
-
#encode64(buff) {
|
|
594
|
-
return btoa(new Uint8Array(buff).reduce((s, b) => s + String.fromCharCode(b), ''));
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
/*
|
|
598
|
-
* Base64 url safe encoding of an array
|
|
599
|
-
*/
|
|
600
|
-
#base64UrlSafeEncode(buf) {
|
|
601
|
-
return this.#encode64(buf).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
/* Calc code verifier if necessary
|
|
605
|
-
*/
|
|
606
|
-
/* eslint-disable camelcase */
|
|
607
|
-
async #getCodeChallenge(code_verifier) {
|
|
608
|
-
return this.#sha256Hash(code_verifier)
|
|
609
|
-
.then((hashed) => {
|
|
610
|
-
return this.#base64UrlSafeEncode(hashed);
|
|
611
|
-
})
|
|
612
|
-
.catch((error) => {
|
|
613
|
-
console.error(`Error calculation code challenge for PKCE: ${error}`);
|
|
614
|
-
})
|
|
615
|
-
.finally(() => {
|
|
616
|
-
return null;
|
|
617
|
-
});
|
|
618
|
-
}
|
|
619
|
-
/* eslint-enable camelcase */
|
|
620
|
-
|
|
621
|
-
/*
|
|
622
|
-
* Return agent value for POST commands
|
|
623
|
-
*/
|
|
624
|
-
#getAgent() {
|
|
625
|
-
if (this.isNode && this.config.ignoreInvalidCerts) {
|
|
626
|
-
let options = {rejectUnauthorized: false};
|
|
627
|
-
if (this.config.legacyTLS) {
|
|
628
|
-
options.secureOptions = this.crypto.constants.SSL_OP_LEGACY_SERVER_CONNECT;
|
|
629
|
-
}
|
|
630
|
-
return new this.https.Agent(options);
|
|
631
|
-
} else {
|
|
632
|
-
return undefined;
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
export default PegaAuth;
|