@pega/custom-dx-components 2.0.23 → 2.0.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/README.md +1 -1
  2. package/package.json +3 -6
  3. package/src/cli.js +0 -18
  4. package/src/constants.js +2 -32
  5. package/src/tasks/authenticate/helper.js +5 -63
  6. package/src/tasks/authenticate/index.js +50 -107
  7. package/src/tasks/create/index.js +63 -59
  8. package/src/tasks/create/templates/Constellation/Field/Boolean/boolean-format.js.mustache +1 -1
  9. package/src/tasks/create/templates/Constellation/Field/Boolean/demo.stories.jsx.mustache +16 -33
  10. package/src/tasks/create/templates/Constellation/Field/Boolean/index.jsx.mustache +2 -1
  11. package/src/tasks/create/templates/Constellation/Field/Date/date.js.mustache +67 -179
  12. package/src/tasks/create/templates/Constellation/Field/Date/demo.stories.jsx.mustache +17 -46
  13. package/src/tasks/create/templates/Constellation/Field/Date/index.jsx.mustache +1 -3
  14. package/src/tasks/create/templates/Constellation/Field/DateTime/date-time.js.mustache +1 -0
  15. package/src/tasks/create/templates/Constellation/Field/DateTime/{date-utils.js.mustache → date-utils.js} +0 -1
  16. package/src/tasks/create/templates/Constellation/Field/DateTime/date.js.mustache +0 -1
  17. package/src/tasks/create/templates/Constellation/Field/DateTime/demo.stories.jsx.mustache +17 -46
  18. package/src/tasks/create/templates/Constellation/Field/DateTime/index.jsx.mustache +0 -2
  19. package/src/tasks/create/templates/Constellation/Field/Decimal/demo.stories.jsx.mustache +17 -22
  20. package/src/tasks/create/templates/Constellation/Field/Decimal/index.jsx.mustache +1 -2
  21. package/src/tasks/create/templates/Constellation/Field/Decimal-Currency/demo.stories.jsx.mustache +17 -26
  22. package/src/tasks/create/templates/Constellation/Field/Decimal-Currency/index.jsx.mustache +4 -3
  23. package/src/tasks/create/templates/Constellation/Field/Decimal-Percentage/demo.stories.jsx.mustache +17 -22
  24. package/src/tasks/create/templates/Constellation/Field/Decimal-Percentage/index.jsx.mustache +2 -8
  25. package/src/tasks/create/templates/Constellation/Field/Integer/demo.stories.jsx.mustache +17 -22
  26. package/src/tasks/create/templates/Constellation/Field/Integer/index.jsx.mustache +1 -2
  27. package/src/tasks/create/templates/Constellation/Field/Picklist/demo.stories.jsx.mustache +16 -50
  28. package/src/tasks/create/templates/Constellation/Field/Picklist/index.jsx.mustache +1 -44
  29. package/src/tasks/create/templates/Constellation/Field/Picklist/picklist.js.mustache +43 -0
  30. package/src/tasks/create/templates/Constellation/Field/Text/demo.stories.jsx.mustache +16 -23
  31. package/src/tasks/create/templates/Constellation/Field/Text-Email/demo.stories.jsx.mustache +17 -21
  32. package/src/tasks/create/templates/Constellation/Field/Text-Email/index.jsx.mustache +2 -44
  33. package/src/tasks/create/templates/Constellation/Field/Text-Email/text-email.js.mustache +43 -0
  34. package/src/tasks/create/templates/Constellation/Field/Text-Input/{StatusWork.jsx → StatusWork.js} +1 -0
  35. package/src/tasks/create/templates/Constellation/Field/Text-Input/demo.stories.jsx.mustache +17 -21
  36. package/src/tasks/create/templates/Constellation/Field/Text-Input/index.jsx.mustache +3 -46
  37. package/src/tasks/create/templates/Constellation/Field/Text-Input/text-input.js.mustache +43 -0
  38. package/src/tasks/create/templates/Constellation/Field/Text-Paragraph/demo.stories.jsx.mustache +17 -21
  39. package/src/tasks/create/templates/Constellation/Field/Text-Paragraph/index.jsx.mustache +2 -44
  40. package/src/tasks/create/templates/Constellation/Field/Text-Paragraph/text-paragraph.js.mustache +43 -0
  41. package/src/tasks/create/templates/Constellation/Field/Text-Phone/demo.stories.jsx.mustache +17 -19
  42. package/src/tasks/create/templates/Constellation/Field/Text-Phone/index.jsx.mustache +3 -47
  43. package/src/tasks/create/templates/Constellation/Field/Text-Phone/text-phone.js.mustache +43 -0
  44. package/src/tasks/create/templates/Constellation/Field/Text-URL/demo.stories.jsx.mustache +17 -19
  45. package/src/tasks/create/templates/Constellation/Field/Text-URL/index.jsx.mustache +1 -1
  46. package/src/tasks/create/templates/Constellation/Field/Text-URL/{text-url.jsx.mustache → text-url.js.mustache} +1 -2
  47. package/src/tasks/create/templates/Constellation/Field/TimeofDay/demo.stories.jsx.mustache +17 -30
  48. package/src/tasks/create/templates/Constellation/Field/TimeofDay/time-of-day.js.mustache +0 -1
  49. package/src/tasks/create/templates/Constellation/Template/DETAILS/index.jsx.mustache +29 -14
  50. package/src/tasks/create/templates/Constellation/Template/DETAILS/utils.js.mustache +77 -3
  51. package/src/tasks/create/templates/Constellation/Template/DETAILS-region/index.jsx.mustache +29 -14
  52. package/src/tasks/create/templates/Constellation/Template/DETAILS-region/utils.js.mustache +74 -3
  53. package/src/tasks/create/templates/Constellation/Template/FORM/index.jsx.mustache +10 -6
  54. package/src/tasks/create/templates/Constellation/Template/FORM-region/index.jsx.mustache +8 -10
  55. package/src/tasks/create/templates/Constellation/Template/PAGE/{GetNextWork.jsx.mustache → GetNextWork.js.mustache} +0 -6
  56. package/src/tasks/create/templates/Constellation/Template/PAGE/index.jsx.mustache +7 -9
  57. package/src/tasks/create/templates/Constellation/Template/PAGE/utils.js.mustache +1 -4
  58. package/src/tasks/create/templates/Constellation/Widget/CASE/index.jsx.mustache +0 -1
  59. package/src/tasks/create/templates/Constellation/Widget/PAGE/index.jsx.mustache +8 -8
  60. package/src/tasks/create/templates/Constellation/Widget/PAGE-CASE/{Avatar.jsx.mustache → Avatar.js.mustache} +0 -2
  61. package/src/tasks/create/templates/Constellation/Widget/PAGE-CASE/{Operator.jsx.mustache → Operator.js.mustache} +1 -2
  62. package/src/tasks/create/templates/Constellation/Widget/PAGE-CASE/index.jsx.mustache +9 -9
  63. package/src/tasks/delete/helper.js +0 -4
  64. package/src/tasks/delete/index.js +0 -4
  65. package/src/tasks/index.js +0 -3
  66. package/src/tasks/init/index.js +3 -3
  67. package/src/tasks/init/template/.eslintrc.json +5 -186
  68. package/src/tasks/init/template/README.md +59 -75
  69. package/src/tasks/init/template/package.json.mustache +6 -21
  70. package/src/tasks/init/template/tasks.config.json +2 -5
  71. package/src/tasks/list/index.js +0 -4
  72. package/src/tasks/{bundle → publish/bundle}/builder/webpack.config.js +2 -0
  73. package/src/tasks/{bundle → publish/bundle}/index.js +2 -2
  74. package/src/tasks/publish/index.js +1 -14
  75. package/src/util.js +6 -11
  76. package/src/auth.js +0 -563
  77. package/src/tasks/build/helper.js +0 -123
  78. package/src/tasks/build/index.js +0 -54
  79. package/src/tasks/create/templates/Constellation/Field/Boolean/mock.stories.js.mustache +0 -21
  80. package/src/tasks/create/templates/Constellation/Field/Date/DateFormatter.js.mustache +0 -61
  81. package/src/tasks/create/templates/Constellation/Field/Date/FormattedText.jsx.mustache +0 -41
  82. package/src/tasks/create/templates/Constellation/Field/Date/mock.stories.js.mustache +0 -23
  83. package/src/tasks/create/templates/Constellation/Field/DateTime/mock.stories.js.mustache +0 -23
  84. package/src/tasks/create/templates/Constellation/Field/Decimal/mock.stories.js.mustache +0 -21
  85. package/src/tasks/create/templates/Constellation/Field/Decimal-Currency/mock.stories.js.mustache +0 -23
  86. package/src/tasks/create/templates/Constellation/Field/Decimal-Percentage/mock.stories.js.mustache +0 -21
  87. package/src/tasks/create/templates/Constellation/Field/Integer/mock.stories.js.mustache +0 -21
  88. package/src/tasks/create/templates/Constellation/Field/Picklist/mock.stories.js.mustache +0 -67
  89. package/src/tasks/create/templates/Constellation/Field/Text/mock.stories.js.mustache +0 -13
  90. package/src/tasks/create/templates/Constellation/Field/Text-Email/mock.stories.js.mustache +0 -22
  91. package/src/tasks/create/templates/Constellation/Field/Text-Input/mock.stories.js.mustache +0 -21
  92. package/src/tasks/create/templates/Constellation/Field/Text-Paragraph/mock.stories.js.mustache +0 -20
  93. package/src/tasks/create/templates/Constellation/Field/Text-Phone/mock.stories.js.mustache +0 -37
  94. package/src/tasks/create/templates/Constellation/Field/Text-URL/mock.stories.js.mustache +0 -22
  95. package/src/tasks/create/templates/Constellation/Field/TimeofDay/mock.stories.js.mustache +0 -20
  96. package/src/tasks/create/templates/Constellation/Template/DETAILS/StatusWork.jsx.mustache +0 -26
  97. package/src/tasks/create/templates/Constellation/Template/DETAILS/demo.stories.jsx.mustache +0 -118
  98. package/src/tasks/create/templates/Constellation/Template/DETAILS/mock.stories.js.mustache +0 -179
  99. package/src/tasks/create/templates/Constellation/Template/DETAILS-region/StatusWork.jsx.mustache +0 -26
  100. package/src/tasks/create/templates/Constellation/Template/DETAILS-region/demo.stories.jsx.mustache +0 -123
  101. package/src/tasks/create/templates/Constellation/Template/DETAILS-region/mock.stories.js.mustache +0 -215
  102. package/src/tasks/create/templates/Constellation/Template/FORM/demo.stories.jsx.mustache +0 -93
  103. package/src/tasks/create/templates/Constellation/Template/FORM/mock.stories.js.mustache +0 -125
  104. package/src/tasks/create/templates/Constellation/Template/FORM-region/demo.stories.jsx.mustache +0 -100
  105. package/src/tasks/create/templates/Constellation/Template/FORM-region/mock.stories.js.mustache +0 -131
  106. package/src/tasks/create/templates/Constellation/Template/PAGE/demo.stories.jsx.mustache +0 -101
  107. package/src/tasks/create/templates/Constellation/Template/PAGE/mock.stories.js.mustache +0 -94
  108. package/src/tasks/create/templates/Constellation/Widget/CASE/demo.stories.jsx.mustache +0 -78
  109. package/src/tasks/create/templates/Constellation/Widget/CASE/mock.stories.js.mustache +0 -390
  110. package/src/tasks/create/templates/Constellation/Widget/PAGE/demo.stories.jsx.mustache +0 -30
  111. package/src/tasks/create/templates/Constellation/Widget/PAGE/mock.stories.js.mustache +0 -23
  112. package/src/tasks/create/templates/Constellation/Widget/PAGE-CASE/demo.stories.jsx.mustache +0 -68
  113. package/src/tasks/create/templates/Constellation/Widget/PAGE-CASE/mock.stories.js.mustache +0 -51
  114. package/src/tasks/delete-all/helper.js +0 -175
  115. package/src/tasks/delete-all/index.js +0 -49
  116. package/src/tasks/init/template/.eslintignore +0 -8
  117. package/src/tasks/linter/index.js +0 -46
  118. package/src/tasks/publish-all/helper.js +0 -139
  119. package/src/tasks/publish-all/index.js +0 -84
  120. /package/src/tasks/{bundle → publish/bundle}/builder/index.js +0 -0
  121. /package/src/tasks/{bundle → publish/bundle}/builder/modules.js +0 -0
  122. /package/src/tasks/{bundle → publish/bundle}/builder/paths.js +0 -0
@@ -10,8 +10,6 @@ import {
10
10
  getListComponentsQuestions
11
11
  } from './helper.js';
12
12
 
13
- import { showVersion } from '../../util.js';
14
-
15
13
  const getList = async (sourceOfComponents, answers) => {
16
14
  switch (sourceOfComponents) {
17
15
  case SOURCE_OF_COMPONENT_TYPES.LOCAL: {
@@ -34,8 +32,6 @@ const getList = async (sourceOfComponents, answers) => {
34
32
  };
35
33
 
36
34
  export default async options => {
37
- showVersion();
38
-
39
35
  let sourceOfComponentsOption = SOURCE_OF_COMPONENT_TYPES.LOCAL;
40
36
 
41
37
  if (options.params[3] === SOURCE_OF_COMPONENT_TYPES.LOCAL) {
@@ -306,12 +306,14 @@ export default (
306
306
  contextRegExp: /moment$/
307
307
  }),
308
308
  /* Ignore cosmos icons */
309
+
309
310
  new webpack.IgnorePlugin({
310
311
  resourceRegExp: /^\.\/icons$/,
311
312
  contextRegExp: /@pega\/cosmos-react-core\/lib\/components\/Icon$/
312
313
  }),
313
314
 
314
315
  /* Ignore cosmos icons - windows */
316
+
315
317
  new webpack.IgnorePlugin({
316
318
  resourceRegExp: /^\.\/icons$/,
317
319
  contextRegExp: /@pega\\cosmos-react-core\\lib\\components\\Icon$/
@@ -2,8 +2,8 @@ import path from 'path';
2
2
  import fs from 'fs';
3
3
  import { stat } from 'fs/promises';
4
4
 
5
- import { BUILD_CONFIG_JSON_FILENAME } from '../../constants.js';
6
- import { getComponentDirectoryPath } from '../../util.js';
5
+ import { BUILD_CONFIG_JSON_FILENAME } from '../../../constants.js';
6
+ import { getComponentDirectoryPath } from '../../../util.js';
7
7
 
8
8
  import buildComponent from './builder/index.js';
9
9
 
@@ -4,15 +4,10 @@ import chalk from 'chalk';
4
4
 
5
5
  import validate from '../validator/index.js';
6
6
 
7
- import bundleComponent from '../bundle/index.js';
8
- import { lintComponent } from '../linter/index.js';
9
-
10
- import { getComponentDirectoryPath, showVersion } from '../../util.js';
7
+ import bundleComponent from './bundle/index.js';
11
8
  import { getPublishComponentQuestions, publishComponentToServer, zipComponent } from './helper.js';
12
9
 
13
10
  export default async options => {
14
- showVersion();
15
-
16
11
  const questions = await getPublishComponentQuestions();
17
12
  const answers = await inquirer.prompt(questions);
18
13
  const { componentKey, sourceMap, devBuild } = answers;
@@ -29,14 +24,6 @@ export default async options => {
29
24
  await validate(componentKey);
30
25
  }
31
26
  },
32
- {
33
- title: 'Lint component',
34
- task: async () => {
35
- const targetDirectory = await getComponentDirectoryPath(componentKey);
36
- // console.log(`in buildComponent Lint component task: componentKey: ${componentKey} targetDirectory: ${targetDirectory}`);
37
- await lintComponent(targetDirectory);
38
- }
39
- },
40
27
  {
41
28
  title: 'Bundle Component',
42
29
  task: async () => await bundleComponent(componentKey, sourceMap, devBuild),
package/src/util.js CHANGED
@@ -7,8 +7,6 @@ import chalk from 'chalk';
7
7
  import mustache from 'mustache';
8
8
  import pascalCase from 'pascalcase';
9
9
  import fetch from 'node-fetch';
10
- import { createRequire } from 'module';
11
-
12
10
 
13
11
  import {
14
12
  COMPONENTS_DIRECTORY_PATH,
@@ -16,7 +14,8 @@ import {
16
14
  TOKEN_PATH,
17
15
  OOTB_COMPONENT_SERVICE_REST_ENDPOINT,
18
16
  DELETE_COMPONENT_SERVICE_REST_ENDPOINT,
19
- OOTB_COMPONENTS
17
+ OOTB_COMPONENTS,
18
+ TOKEN_URL_V1
20
19
  } from './constants.js';
21
20
 
22
21
  const access = promisify(fs.access);
@@ -219,14 +218,6 @@ export const validateRulesetVersion = str => {
219
218
  return regex.test(str);
220
219
  };
221
220
 
222
- export const showVersion = () => {
223
- // get package version, so can display at start
224
- const require = createRequire(import.meta.url);
225
- const pData = require("@pega/custom-dx-components/package.json");
226
-
227
- console.log(chalk.green("DX Component Builder v" + pData.version));
228
- };
229
-
230
221
  export const getOOTBComponents = async () => {
231
222
  return new Promise((resolve, reject) => {
232
223
  fs.readFile(OOTB_COMPONENTS, 'utf-8', async (err, data) => {
@@ -280,3 +271,7 @@ export const getOOTBComponents = async () => {
280
271
  });
281
272
  });
282
273
  };
274
+
275
+ export const getTokenUrl = async (server) => {
276
+ return server.endsWith('/') ? `${server}${TOKEN_URL_V1}` : `${server}/${TOKEN_URL_V1}`;
277
+ };
package/src/auth.js DELETED
@@ -1,563 +0,0 @@
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 'crypto';
7
- import http from 'http';
8
-
9
-
10
- class PegaAuth {
11
- constructor(ssKeyConfig) {
12
- if (typeof ssKeyConfig === "string") {
13
- this.ssKeyConfig = ssKeyConfig;
14
- this.#reloadConfig();
15
- } else {
16
- // object with config structure is passed in
17
- this.config = ssKeyConfig;
18
- }
19
- this.isNode = typeof window === 'undefined';
20
- this.crypto = this.isNode ? crypto : window.crypto;
21
-
22
- // Tried both preferring the subtle and the webcrypto but both still yileded different behavior on digest
23
-
24
- // Prefer webcrypto.subtle over regular subtle
25
- // this.subtle = this.isNode ? (crypto.webcrypto ? crypto.webcrypto.subtle : crypto.subtle) : window.crypto.subtle;
26
-
27
- // Prefer regular subtle over webcrypto.suble for now
28
- this.subtle = this.isNode ? (crypto.subtle ? crypto.subtle : crypto.webcrypto.subtle) : window.crypto.subtle;
29
- }
30
-
31
- #reloadConfig() {
32
- if (!this.ssKeyConfig) {
33
- return;
34
- }
35
- const peConfig = window.sessionStorage.getItem(this.ssKeyConfig);
36
- let obj = {};
37
- if (peConfig) {
38
- try {
39
- obj = JSON.parse(peConfig);
40
- } catch (e) {
41
- try {
42
- obj = JSON.parse(this.#atob(peConfig));
43
- } catch (e) {
44
- obj = {};
45
- }
46
- }
47
- }
48
- this.config = peConfig ? obj : {};
49
- }
50
-
51
- #updateConfig() {
52
- if (this.ssKeyConfig) {
53
- window.sessionStorage.setItem(this.ssKeyConfig, this.#btoa(JSON.stringify(this.config)));
54
- }
55
- }
56
-
57
- // For PKCE the authorize includes a code_challenge & code_challenge_method as well
58
- async #buildAuthorizeUrl(state) {
59
- const { serverType, clientId, redirectUri, authorizeUri, authService, sessionIndex, appAlias, userIdentifier, password, noPKCE } =
60
- this.config;
61
-
62
- if (!noPKCE) {
63
- // Generate random string of 64 chars for verifier. RFC 7636 says from 43-128 chars
64
- const buf = new Uint8Array(64);
65
- this.crypto.getRandomValues(buf);
66
- this.config.codeVerifier = this.#base64UrlSafeEncode(buf);
67
- }
68
-
69
- // If sessionIndex exists then increment attempts count (we will stop sending session_index after two failures)
70
- if (sessionIndex) {
71
- this.config.sessionIndexAttempts += 1;
72
- }
73
- // Persist codeVerifier in session storage so it survives the redirects that are to follow
74
- this.#updateConfig();
75
-
76
- if (!state) {
77
- // Calc random state variable
78
- const buf = new Uint8Array(32);
79
- this.crypto.getRandomValues(buf);
80
- state = this.#base64UrlSafeEncode(buf);
81
- }
82
-
83
- // Trim alias to include just the real alias piece
84
- const addtlScope = appAlias ? '+app.alias.' + appAlias.replace(/^app\//, '') : '';
85
- const scope = (serverType === 'infinity' ? `openid${addtlScope}` : 'user_info');
86
-
87
- // Add explicit creds if specified to try to avoid login popup
88
- const moreAuthArgs = (serverType === 'infinity' ?
89
- '&enable_psyncid=true' +
90
- (authService ? `&authentication_service=${encodeURIComponent(authService)}` : '') +
91
- (sessionIndex && this.config.sessionIndexAttempts < 3 ? `&session_index=${sessionIndex}` : '') +
92
- (userIdentifier
93
- ? `&UserIdentifier=${encodeURIComponent(userIdentifier)}${password ? `&Password=${encodeURIComponent(this.#atob(password))}` : ''}` : '')
94
- : '');
95
-
96
- let pkceArgs = '';
97
- if (!noPKCE) {
98
- const cc = await this.#getCodeChallenge(this.config.codeVerifier);
99
- pkceArgs = `&code_challenge=${cc}&code_challenge_method=S256`;
100
- }
101
- const authUrl = `${authorizeUri}?client_id=${clientId}&response_type=code&redirect_uri=${redirectUri}&scope=${scope}&state=${state}${pkceArgs}${moreAuthArgs}`;
102
- // console.log(`authUrl: ${authUrl}`);
103
- return authUrl;
104
- }
105
-
106
- async login() {
107
- const { grantType } = this.config;
108
- if (grantType && grantType !== 'authCode') {
109
- return this.getToken();
110
- } else {
111
- return this.#authCodeStart();
112
- }
113
- }
114
-
115
- // authCode login issues the authorize endpoint transaction and deals with redirects
116
- async #authCodeStart() {
117
- const fnGetRedirectUriOrigin = () => {
118
- let redirectUri = this.config.redirectUri;
119
- let nRootOffset = redirectUri.indexOf("//");
120
- let nFirstPathOffset = nRootOffset !== -1 ? redirectUri.indexOf("/", nRootOffset + 2) : -1;
121
- return nFirstPathOffset !== -1 ? redirectUri.substring(0, nFirstPathOffset) : redirectUri;
122
- };
123
-
124
- const redirectOrigin = fnGetRedirectUriOrigin();
125
- const state = this.isNode ? '' : this.#btoa(location.origin);
126
-
127
- return new Promise((resolve, reject) => {
128
- /* eslint-disable consistent-return */
129
- this.#buildAuthorizeUrl(state).then((url) => {
130
- let myWindow = null; // popup or iframe
131
- let elIframe = null,
132
- elCloseBtn = null;
133
- const iframeTimeout = this.config.silentTimeout !== undefined ? this.config.silentTimeout : 5000;
134
- let bWinIframe = !this.isNode &&
135
- iframeTimeout > 0 &&
136
- ((!!this.config.userIdentifier && !!this.config.password) ||
137
- this.config.iframeLoginUI ||
138
- this.config.authService !== 'pega');
139
- let tmrAuthComplete = null,
140
- checkWindowClosed = null;
141
- const myWinOnLoad = () => {
142
- try {
143
- if (bWinIframe) {
144
- elIframe.contentWindow.postMessage({ type: "PegaAuth" }, redirectOrigin);
145
- console.log("authjs(login): loaded a page in iFrame");
146
- } else {
147
- myWindow.postMessage({ type: "PegaAuth" }, redirectOrigin);
148
- }
149
- } catch (e) {
150
- console.log("authjs(login): Exception trying to postMessage on load");
151
- }
152
- };
153
- const fnOpenPopup = () => {
154
- myWindow = (this.isNode ? open : window.open)(url, '_blank', 'width=700,height=500,left=200,top=100');
155
- if (!myWindow) {
156
- // Blocked by popup-blocker
157
- return reject("blocked");
158
- }
159
- checkWindowClosed = setInterval(() => {
160
- if (myWindow.closed) {
161
- clearInterval(checkWindowClosed);
162
- reject("closed");
163
- }
164
- }, 500);
165
- if (!this.isNode) {
166
- try {
167
- myWindow.addEventListener("load", myWinOnLoad, true);
168
- } catch (e) {
169
- console.log("authjs(login): Exception trying to add onload handler to opened window;");
170
- }
171
- }
172
- };
173
- const fnCloseIframe = () => {
174
- elIframe.parentNode.removeChild(elIframe);
175
- elCloseBtn.parentNode.removeChild(elCloseBtn);
176
- elIframe = elCloseBtn = null;
177
- bWinIframe = false;
178
- };
179
- const fnCloseAndReject = () => {
180
- fnCloseIframe();
181
- reject("closed");
182
- };
183
- let authMessageReceiver = null;
184
- /* Retrieve token(s) and close login window */
185
- const fnGetTokenAndFinish = (code) => {
186
- if (!this.isNode) {
187
- window.removeEventListener("message", authMessageReceiver, false);
188
- }
189
- this.getToken(code).then(token => {
190
- if (bWinIframe) {
191
- clearTimeout(tmrAuthComplete);
192
- fnCloseIframe();
193
- } else {
194
- if (!this.isNode) {
195
- clearInterval(checkWindowClosed);
196
- myWindow.close();
197
- }
198
- }
199
- resolve(token);
200
- })
201
- .catch(e => {
202
- reject(e);
203
- });
204
- };
205
- // If there is a userIdentifier and password specified or an external SSO auth service,
206
- // we can try to use this silently in an iFrame first
207
- if (bWinIframe) {
208
- const nFrameZLevel = 99999;
209
- elIframe = document.createElement('iframe');
210
- elIframe.id = 'pe' + this.config.clientId;
211
- const loginBoxWidth = 500,
212
- loginBoxHeight = 700;
213
- const oStyle = elIframe.style;
214
- oStyle.position = 'absolute';
215
- oStyle.display = 'none';
216
- oStyle.zIndex = nFrameZLevel;
217
- oStyle.top = `${Math.round(Math.max(window.innerHeight - loginBoxHeight, 0) / 2)}px`;
218
- oStyle.left = `${Math.round(Math.max(window.innerWidth - loginBoxWidth, 0) / 2)}px`;
219
- oStyle.width = '500px';
220
- oStyle.height = '700px';
221
- // Add Iframe to top of document DOM to have it load
222
- document.body.insertBefore(elIframe, document.body.firstChild);
223
- // document.getElementsByTagName('body')[0].appendChild(elIframe);
224
- elIframe.addEventListener('load', myWinOnLoad, true);
225
- // Disallow iframe content attempts to navigate main window
226
- elIframe.setAttribute('sandbox', 'allow-scripts allow-forms allow-same-origin');
227
- elIframe.setAttribute('src', url);
228
-
229
- const svgCloseBtn = `<?xml version="1.0" encoding="UTF-8"?>
230
- <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">
231
- <title>Dismiss - Black</title>
232
- <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
233
- <g transform="translate(1.000000, 1.000000)">
234
- <circle fill="#252C32" cx="16" cy="16" r="16"></circle>
235
- <g transform="translate(9.109375, 9.214844)" fill="#FFFFFF" fill-rule="nonzero">
236
- <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>
237
- </g>
238
- </g>
239
- </g>
240
- </svg>`;
241
- const bCloseWithinFrame = false;
242
- elCloseBtn = document.createElement('img');
243
- elCloseBtn.onclick = fnCloseAndReject;
244
- elCloseBtn.src = `data:image/svg+xml;base64,${this.#btoa(svgCloseBtn)}`;
245
- const oBtnStyle = elCloseBtn.style;
246
- oBtnStyle.cursor = 'pointer';
247
- // If svg doesn't set width and height might want to set oBtStyle width and height to something like '2em'
248
- oBtnStyle.position = 'absolute';
249
- oBtnStyle.display = 'none';
250
- oBtnStyle.zIndex = nFrameZLevel + 1;
251
- const nTopOffset = bCloseWithinFrame ? 5 : -10;
252
- const nRightOffset = bCloseWithinFrame ? -34 : -20;
253
- oBtnStyle.top = `${Math.round(Math.max(window.innerHeight - loginBoxHeight, 0) / 2) + nTopOffset}px`;
254
- oBtnStyle.left = `${Math.round(Math.max(window.innerWidth - loginBoxWidth, 0) / 2) + loginBoxWidth + nRightOffset
255
- }px`;
256
- document.body.insertBefore(elCloseBtn, document.body.firstChild);
257
- // If the password was wrong, then the login screen will be in the iframe
258
- // ..and with Pega without realization of US-372314 it may replace the top (main portal) window
259
- // For now set a timer and if the timer expires, remove the iFrame and use same url within
260
- // visible window
261
- tmrAuthComplete = setTimeout(() => {
262
- clearTimeout(tmrAuthComplete);
263
- // remove password from config
264
- if (this.config.password) {
265
- delete this.config.password;
266
- this.#updateConfig();
267
- }
268
- // Display the iframe where the redirects did not succeed (or invoke a popup window)
269
- if (this.config.iframeLoginUI) {
270
- elIframe.style.display = 'block';
271
- elCloseBtn.style.display = 'block';
272
- } else {
273
- fnCloseIframe();
274
- fnOpenPopup();
275
- }
276
- }, iframeTimeout);
277
- } else {
278
- if (this.isNode) {
279
- // Determine port to listen to by extracting it from redirect uri
280
- const { redirectUri } = this.config;
281
- const nLocalhost = redirectUri.indexOf("localhost:");
282
- const nSlash = redirectUri.indexOf('/', nLocalhost + 10);
283
- const nPort = parseInt(redirectUri.substring(nLocalhost + 10, nSlash));
284
- if (-1 !== nLocalhost) {
285
- const server = http.createServer((req, res) => {
286
- const { method, url } = req;
287
- const { winTitle, winBodyHtml } = this.config;
288
- // console.log(`HTTP request received: ${url}`);
289
- res.writeHead(200, { 'Content-Type': 'text/html' });
290
- // Auto closing window for now. Can always leave it up and allow authConfig props to set title and bodyHtml
291
- res.end(`<html><head><title>${winTitle}</title><script>window.close();</script></head><body>${winBodyHtml}</body></html>`);
292
- const queryString = url.split('?')[1];
293
- const urlParams = new URLSearchParams(queryString);
294
- const code = urlParams.get('code');
295
- if (code) {
296
- // console.log(`Auth code received: ${code}`);
297
- // Stop receiving connections and close when all are handled.
298
- server.close();
299
- fnGetTokenAndFinish(code);
300
- }
301
- });
302
- server.listen(nPort);
303
- }
304
- }
305
- fnOpenPopup();
306
- }
307
-
308
- if (!this.isNode) {
309
- /* Handler to receive the auth code */
310
- authMessageReceiver = (event) => {
311
- // Check origin to make sure it is the redirect origin
312
- if (event.origin !== redirectOrigin) return;
313
- if (!event.data || !event.data.type || event.data.type !== 'PegaAuth') return;
314
- console.log('authjs(login): postMessage received with code');
315
- const code = event.data.code.toString();
316
- fnGetTokenAndFinish(code);
317
- };
318
- window.addEventListener('message', authMessageReceiver, false);
319
- window.authCodeCallback = function (code) {
320
- console.log('authjs(login): authCodeCallback used with code');
321
- fnGetTokenAndFinish(code);
322
- };
323
- }
324
- });
325
- });
326
- }
327
-
328
- // For PKCE token endpoint includes code_verifier
329
- getToken(authCode) {
330
- // Reload config to pick up the previously stored codeVerifier
331
- this.#reloadConfig();
332
-
333
- const { serverType, isolationId, clientId, clientSecret, redirectUri, tokenUri, codeVerifier, grantType,
334
- sessionIndex, customTokenParams, userIdentifier, password, noPKCE } = this.config;
335
-
336
- const bAuthCode = !grantType || grantType === 'authCode';
337
- if (bAuthCode && !authCode && !this.isNode) {
338
- const queryString = window.location.search;
339
- const urlParams = new URLSearchParams(queryString);
340
- authCode = urlParams.get('code');
341
- }
342
-
343
- const formData = new URLSearchParams();
344
- formData.append('client_id', clientId);
345
- if (clientSecret) {
346
- formData.append('client_secret', clientSecret);
347
- }
348
- const fullGTName = {
349
- "authCode": "authorization_code",
350
- "clientCreds": "client_credentials",
351
- "customBearer": "custom-bearer",
352
- "passwordCreds": "password"
353
- }[grantType];
354
- const grant_type = fullGTName ? fullGTName :
355
- (grantType ? grantType : 'authorization_code');
356
- formData.append('grant_type', grant_type);
357
- if (serverType === 'launchpad' && grantType !== 'authCode') {
358
- formData.append('isolation_ids', isolationId);
359
- }
360
- if (bAuthCode) {
361
- formData.append('code', authCode);
362
- formData.append('redirect_uri', redirectUri);
363
- if (!noPKCE) {
364
- formData.append('code_verifier', codeVerifier);
365
- }
366
- } else if (sessionIndex) {
367
- formData.append('session_index', sessionIndex);
368
- }
369
- if (grantType === 'customBearer' && customTokenParams) {
370
- Object.keys(customTokenParams).forEach((param) => {
371
- formData.append(param, customTokenParams[param]);
372
- });
373
- }
374
- if (grantType === 'passwordCreds') {
375
- formData.append('username', userIdentifier);
376
- formData.append('password', this.#atob(password));
377
- }
378
- // console.log(`getToken form data: ${formData.toString()}`);
379
-
380
- return fetch(tokenUri, {
381
- method: 'POST',
382
- headers: new Headers({
383
- 'content-type': 'application/x-www-form-urlencoded'
384
- }),
385
-
386
- body: formData.toString()
387
- })
388
- .then((response) => response.json())
389
- .then((token) => {
390
- if (token.errors || token.error) {
391
- // console.log(`formData: ${formData.toString()}`);
392
- // console.log(`Token endpoint error: ${JSON.stringify(token.errors ? token.errors : token)}`);
393
- } else {
394
- // .expires_in contains the # of seconds before access token expires
395
- // add property to keep track of current time when the token expires
396
- /* eslint-disable camelcase */
397
- token.eA = Date.now() + token.expires_in * 1000;
398
- if (this.config.codeVerifier) {
399
- delete this.config.codeVerifier;
400
- }
401
- // If there is a session_index then move this to the peConfig structure (as used on authorize)
402
- if (token.session_index) {
403
- this.config.sessionIndex = token.session_index;
404
- }
405
- // If we got a token and have a session index, then reset the sessionIndexAttempts
406
- if (this.config.sessionIndex) {
407
- this.config.sessionIndexAttempts = 0;
408
- }
409
- this.#updateConfig();
410
- }
411
- return token;
412
- /* eslint-enable camelcase */
413
- })
414
- .catch((e) => {
415
- console.log(e);
416
- });
417
- }
418
-
419
- /* eslint-disable camelcase */
420
- async refreshToken(refresh_token) {
421
- const { clientId, clientSecret, tokenUri } = this.config;
422
-
423
- const formData = new URLSearchParams();
424
- formData.append('client_id', clientId);
425
- if (clientSecret) {
426
- formData.append('client_secret', clientSecret);
427
- }
428
- formData.append('grant_type', 'refresh_token');
429
- formData.append('refresh_token', refresh_token);
430
-
431
- return fetch(tokenUri, {
432
- method: 'POST',
433
- headers: new Headers({
434
- 'content-type': 'application/x-www-form-urlencoded'
435
- }),
436
-
437
- body: formData.toString()
438
- })
439
- .then((response) => {
440
- if (!response.ok && 401 === response.status) {
441
- return null;
442
- }
443
- return response.json();
444
- })
445
- .then((token) => {
446
- if (token) {
447
- // .expires_in contains the # of seconds before access token expires
448
- // add property to keep track of current time when the token expires
449
- token.eA = Date.now() + token.expires_in * 1000;
450
- }
451
- return token;
452
- })
453
- .catch((e) => {
454
- console.log(e);
455
- });
456
- }
457
-
458
- async revokeTokens(access_token, refresh_token = null) {
459
- if (Object.keys(this.config).length === 0) {
460
- // Must have a config structure to proceed
461
- return;
462
- }
463
- const { clientId, clientSecret, revokeUri } = this.config;
464
-
465
- let hdrs = { 'content-type': 'application/x-www-form-urlencoded' };
466
- if (clientSecret) {
467
- hdrs['authorization'] = 'Basic ' + this.#btoa(clientId + ':' + clientSecret);
468
- }
469
- let aTknProps = ['access_token'];
470
- if (refresh_token) {
471
- aTknProps.push('refresh_token');
472
- }
473
- aTknProps.forEach((prop) => {
474
- const formData = new URLSearchParams();
475
- if (!clientSecret) {
476
- formData.append('client_id', clientId);
477
- }
478
- formData.append('token', prop === 'access_token' ? access_token : refresh_token);
479
- formData.append('token_type_hint', prop);
480
- fetch(revokeUri, {
481
- method: 'POST',
482
- headers: new Headers(hdrs),
483
- body: formData.toString()
484
- })
485
- .then((response) => {
486
- if (!response.ok) {
487
- console.log('Error revoking ' + prop + ':' + response.status);
488
- }
489
- })
490
- .catch((e) => {
491
- console.log(e);
492
- });
493
- });
494
- // Also clobber any sessionIndex
495
- if (this.config.sessionIndex) {
496
- delete this.config.sessionIndex;
497
- this.#updateConfig();
498
- }
499
- }
500
- /* eslint-enable camelcase */
501
-
502
- #sha256Hash(str) {
503
- // Found that the Node implementation of subtle.digest is yielding incorrect results
504
- // so using a different set of apis to get expected results.
505
- if (this.isNode) {
506
- return new Promise((resolve) => {
507
- return resolve(this.crypto.createHash("sha256").update(str).digest());
508
- });
509
- } else {
510
- return this.subtle.digest('SHA-256', new TextEncoder().encode(str));
511
- }
512
- }
513
-
514
- /* btoa to work with node or browser - create B64 string*/
515
- #btoa(str) {
516
- if (this.isNode) {
517
- return Buffer.from(str, 'binary').toString('base64');
518
- } else {
519
- return window.btoa(str);
520
- }
521
- }
522
-
523
- /* atoa to work with node or browser - convert B64 to binary */
524
- #atob(str) {
525
- if (this.isNode) {
526
- return Buffer.from(str, 'base64').toString('binary');
527
- } else {
528
- return window.atob(str);
529
- }
530
- }
531
-
532
- // Base64 encode
533
- /* eslint-disable-next-line class-methods-use-this */
534
- #encode64(buff) {
535
- return this.#btoa(new Uint8Array(buff).reduce((s, b) => s + String.fromCharCode(b), ''));
536
- }
537
-
538
- /*
539
- * Base64 url safe encoding of an array
540
- */
541
- #base64UrlSafeEncode(buf) {
542
- return this.#encode64(buf).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
543
- }
544
-
545
- /* Calc code verifier if necessary
546
- */
547
- /* eslint-disable camelcase */
548
- #getCodeChallenge(code_verifier) {
549
- return this.#sha256Hash(code_verifier)
550
- .then((hashed) => {
551
- return this.#base64UrlSafeEncode(hashed);
552
- })
553
- .catch((error) => {
554
- console.log(error);
555
- })
556
- .finally(() => {
557
- return null;
558
- });
559
- }
560
- /* eslint-enable camelcase */
561
- }
562
-
563
- export default PegaAuth;