@pega/dx-component-builder-sdk 0.1.47 → 0.1.49

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 (268) hide show
  1. package/package.json +3 -2
  2. package/src/auth.js +726 -508
  3. package/src/cli.js +1 -1
  4. package/src/constants.js +728 -2
  5. package/src/tasks/authenticate/index.js +12 -2
  6. package/src/tasks/build/helper.js +10 -6
  7. package/src/tasks/build/index.js +76 -22
  8. package/src/tasks/build-all/helper.js +16 -11
  9. package/src/tasks/build-all/index.js +106 -42
  10. package/src/tasks/bundle/{builder → builderAngular}/paths.js +3 -2
  11. package/src/tasks/bundle/builderAngular/webpack.config.js +342 -0
  12. package/src/tasks/bundle/builderAssociated/index.js +218 -0
  13. package/src/tasks/bundle/builderAssociated/modules.js +139 -0
  14. package/src/tasks/bundle/builderAssociated/paths.js +51 -0
  15. package/src/tasks/bundle/{builder → builderAssociated}/webpack.config.js +5 -4
  16. package/src/tasks/bundle/builderReact/index.js +218 -0
  17. package/src/tasks/bundle/builderReact/modules.js +139 -0
  18. package/src/tasks/bundle/builderReact/paths.js +51 -0
  19. package/src/tasks/bundle/builderReact/webpack.config.js +337 -0
  20. package/src/tasks/bundle/index.js +122 -5
  21. package/src/tasks/create/index.js +54 -12
  22. package/src/tasks/create/templates/Constellation/field/Boolean/styles.js.mustache +13 -6
  23. package/src/tasks/create/templates/Constellation/field/Date/DateFormatter.js.mustache +7 -7
  24. package/src/tasks/create/templates/Constellation/field/Date/date.js.mustache +8 -8
  25. package/src/tasks/create/templates/Constellation/field/Date/styles.js.mustache +13 -6
  26. package/src/tasks/create/templates/Constellation/field/DateTime/date-time.js.mustache +10 -10
  27. package/src/tasks/create/templates/Constellation/field/DateTime/date-utils.js.mustache +6 -5
  28. package/src/tasks/create/templates/Constellation/field/DateTime/date.js.mustache +6 -3
  29. package/src/tasks/create/templates/Constellation/field/DateTime/styles.js.mustache +13 -6
  30. package/src/tasks/create/templates/Constellation/field/Decimal/index.jsx.mustache +2 -2
  31. package/src/tasks/create/templates/Constellation/field/Decimal/styles.js.mustache +13 -6
  32. package/src/tasks/create/templates/Constellation/field/Decimal-Currency/index.jsx.mustache +2 -2
  33. package/src/tasks/create/templates/Constellation/field/Decimal-Currency/styles.js.mustache +13 -6
  34. package/src/tasks/create/templates/Constellation/field/Decimal-Percentage/index.jsx.mustache +4 -4
  35. package/src/tasks/create/templates/Constellation/field/Decimal-Percentage/styles.js.mustache +13 -6
  36. package/src/tasks/create/templates/Constellation/field/Integer/index.jsx.mustache +2 -2
  37. package/src/tasks/create/templates/Constellation/field/Integer/styles.js.mustache +13 -6
  38. package/src/tasks/create/templates/Constellation/field/Picklist/styles.js.mustache +13 -6
  39. package/src/tasks/create/templates/Constellation/field/Text/styles.js.mustache +13 -6
  40. package/src/tasks/create/templates/Constellation/field/Text-Email/styles.js.mustache +13 -6
  41. package/src/tasks/create/templates/Constellation/field/Text-Input/styles.js.mustache +13 -6
  42. package/src/tasks/create/templates/Constellation/field/Text-Paragraph/styles.js.mustache +13 -6
  43. package/src/tasks/create/templates/Constellation/field/Text-Phone/index.jsx.mustache +0 -1
  44. package/src/tasks/create/templates/Constellation/field/Text-Phone/styles.js.mustache +13 -6
  45. package/src/tasks/create/templates/Constellation/field/Text-URL/styles.js.mustache +13 -6
  46. package/src/tasks/create/templates/Constellation/field/TimeOfDay/styles.js.mustache +14 -0
  47. package/src/tasks/create/templates/Constellation/field/{TimeofDay → TimeOfDay}/time-of-day.js.mustache +6 -5
  48. package/src/tasks/create/templates/Constellation/template/DETAILS/demo.stories.jsx.mustache +20 -6
  49. package/src/tasks/create/templates/Constellation/template/DETAILS/index.jsx.mustache +2 -2
  50. package/src/tasks/create/templates/Constellation/template/DETAILS/mock.stories.js.mustache +20 -10
  51. package/src/tasks/create/templates/Constellation/template/DETAILS/styles.js.mustache +13 -6
  52. package/src/tasks/create/templates/Constellation/template/DETAILS-region/demo.stories.jsx.mustache +20 -7
  53. package/src/tasks/create/templates/Constellation/template/DETAILS-region/index.jsx.mustache +2 -2
  54. package/src/tasks/create/templates/Constellation/template/DETAILS-region/mock.stories.js.mustache +28 -13
  55. package/src/tasks/create/templates/Constellation/template/DETAILS-region/styles.js.mustache +13 -6
  56. package/src/tasks/create/templates/Constellation/template/FORM/demo.stories.jsx.mustache +92 -16
  57. package/src/tasks/create/templates/Constellation/template/FORM/index.jsx.mustache +2 -2
  58. package/src/tasks/create/templates/Constellation/template/FORM/mock.stories.js.mustache +24 -12
  59. package/src/tasks/create/templates/Constellation/template/FORM/styles.js.mustache +13 -6
  60. package/src/tasks/create/templates/Constellation/template/FORM-region/demo.stories.jsx.mustache +91 -13
  61. package/src/tasks/create/templates/Constellation/template/FORM-region/index.jsx.mustache +2 -2
  62. package/src/tasks/create/templates/Constellation/template/FORM-region/mock.stories.js.mustache +24 -12
  63. package/src/tasks/create/templates/Constellation/template/FORM-region/styles.js.mustache +13 -6
  64. package/src/tasks/create/templates/Constellation/template/PAGE/demo.stories.jsx.mustache +1 -0
  65. package/src/tasks/create/templates/Constellation/template/PAGE/styles.js.mustache +13 -6
  66. package/src/tasks/create/templates/Constellation/widget/CASE/index.jsx.mustache +2 -2
  67. package/src/tasks/create/templates/Constellation/widget/CASE/styles.js.mustache +13 -6
  68. package/src/tasks/create/templates/Constellation/widget/PAGE/styles.js.mustache +13 -6
  69. package/src/tasks/create/templates/Constellation/widget/PAGE-CASE/Operator.jsx.mustache +2 -2
  70. package/src/tasks/create/templates/Constellation/widget/PAGE-CASE/index.jsx.mustache +2 -2
  71. package/src/tasks/create/templates/Constellation/widget/PAGE-CASE/styles.js.mustache +13 -6
  72. package/src/tasks/create/templates/SDK-Angular/field/Boolean/boolean.component.html.mustache +42 -0
  73. package/src/tasks/create/templates/SDK-Angular/field/Boolean/boolean.component.scss.mustache +24 -0
  74. package/src/tasks/create/templates/SDK-Angular/field/Boolean/boolean.component.spec.ts.mustache +25 -0
  75. package/src/tasks/create/templates/SDK-Angular/field/Boolean/boolean.component.ts.mustache +165 -0
  76. package/src/tasks/create/templates/SDK-Angular/field/Boolean/config.json.mustache +87 -0
  77. package/src/tasks/create/templates/SDK-Angular/field/Boolean/demo.stories.jsx.mustache +56 -0
  78. package/src/tasks/create/templates/SDK-Angular/field/Boolean/mock.stories.js.mustache +15 -0
  79. package/src/tasks/create/templates/SDK-Angular/field/Date/config.json.mustache +147 -0
  80. package/src/tasks/create/templates/SDK-Angular/field/Date/date.component.html.mustache +35 -0
  81. package/src/tasks/create/templates/SDK-Angular/field/Date/date.component.scss.mustache +20 -0
  82. package/src/tasks/create/templates/SDK-Angular/field/Date/date.component.spec.ts.mustache +25 -0
  83. package/src/tasks/create/templates/SDK-Angular/field/Date/date.component.ts.mustache +209 -0
  84. package/src/tasks/create/templates/SDK-Angular/field/DateTime/config.json.mustache +204 -0
  85. package/src/tasks/create/templates/SDK-Angular/field/DateTime/date-time.component.html.mustache +31 -0
  86. package/src/tasks/create/templates/SDK-Angular/field/DateTime/date-time.component.scss.mustache +20 -0
  87. package/src/tasks/create/templates/SDK-Angular/field/DateTime/date-time.component.spec.ts.mustache +25 -0
  88. package/src/tasks/create/templates/SDK-Angular/field/DateTime/date-time.component.ts.mustache +177 -0
  89. package/src/tasks/create/templates/SDK-Angular/field/Decimal/config.json.mustache +94 -0
  90. package/src/tasks/create/templates/SDK-Angular/field/Decimal/decimal.component.html.mustache +32 -0
  91. package/src/tasks/create/templates/SDK-Angular/field/Decimal/decimal.component.scss.mustache +20 -0
  92. package/src/tasks/create/templates/SDK-Angular/field/Decimal/decimal.component.spec.ts.mustache +25 -0
  93. package/src/tasks/create/templates/SDK-Angular/field/Decimal/decimal.component.ts.mustache +170 -0
  94. package/src/tasks/create/templates/SDK-Angular/field/Decimal-Currency/config.json.mustache +125 -0
  95. package/src/tasks/create/templates/SDK-Angular/field/Decimal-Currency/decimal-currency.component.html.mustache +33 -0
  96. package/src/tasks/create/templates/SDK-Angular/field/Decimal-Currency/decimal-currency.component.scss.mustache +20 -0
  97. package/src/tasks/create/templates/SDK-Angular/field/Decimal-Currency/decimal-currency.component.spec.ts.mustache +25 -0
  98. package/src/tasks/create/templates/SDK-Angular/field/Decimal-Currency/decimal-currency.component.ts.mustache +175 -0
  99. package/src/tasks/create/templates/SDK-Angular/field/Decimal-Percentage/config.json.mustache +94 -0
  100. package/src/tasks/create/templates/SDK-Angular/field/Decimal-Percentage/decimal-percentage.component.html.mustache +33 -0
  101. package/src/tasks/create/templates/SDK-Angular/field/Decimal-Percentage/decimal-percentage.component.scss.mustache +20 -0
  102. package/src/tasks/create/templates/SDK-Angular/field/Decimal-Percentage/decimal-percentage.component.spec.ts.mustache +25 -0
  103. package/src/tasks/create/templates/SDK-Angular/field/Decimal-Percentage/decimal-percentage.component.ts.mustache +176 -0
  104. package/src/tasks/create/templates/SDK-Angular/field/Integer/config.json.mustache +88 -0
  105. package/src/tasks/create/templates/SDK-Angular/field/Integer/integer.component.html.mustache +33 -0
  106. package/src/tasks/create/templates/SDK-Angular/field/Integer/integer.component.scss.mustache +20 -0
  107. package/src/tasks/create/templates/SDK-Angular/field/Integer/integer.component.spec.ts.mustache +25 -0
  108. package/src/tasks/create/templates/SDK-Angular/field/Integer/integer.component.ts.mustache +180 -0
  109. package/src/tasks/create/templates/SDK-Angular/field/Picklist/config.json.mustache +86 -0
  110. package/src/tasks/create/templates/SDK-Angular/field/Picklist/picklist.component.html.mustache +30 -0
  111. package/src/tasks/create/templates/SDK-Angular/field/Picklist/picklist.component.scss.mustache +25 -0
  112. package/src/tasks/create/templates/SDK-Angular/field/Picklist/picklist.component.spec.ts.mustache +25 -0
  113. package/src/tasks/create/templates/SDK-Angular/field/Picklist/picklist.component.ts.mustache +195 -0
  114. package/src/tasks/create/templates/SDK-Angular/field/Text/config.json.mustache +83 -0
  115. package/src/tasks/create/templates/SDK-Angular/field/Text/text.component.html.mustache +17 -0
  116. package/src/tasks/create/templates/SDK-Angular/field/Text/text.component.scss.mustache +30 -0
  117. package/src/tasks/create/templates/SDK-Angular/field/Text/text.component.spec.ts.mustache +25 -0
  118. package/src/tasks/create/templates/SDK-Angular/field/Text/text.component.ts.mustache +145 -0
  119. package/src/tasks/create/templates/SDK-Angular/field/Text-Email/config.json.mustache +83 -0
  120. package/src/tasks/create/templates/SDK-Angular/field/Text-Email/text-email.component.html.mustache +31 -0
  121. package/src/tasks/create/templates/SDK-Angular/field/Text-Email/text-email.component.scss.mustache +20 -0
  122. package/src/tasks/create/templates/SDK-Angular/field/Text-Email/text-email.component.spec.ts.mustache +25 -0
  123. package/src/tasks/create/templates/SDK-Angular/field/Text-Email/text-email.component.ts.mustache +175 -0
  124. package/src/tasks/create/templates/SDK-Angular/field/Text-Input/config.json.mustache +83 -0
  125. package/src/tasks/create/templates/SDK-Angular/field/Text-Input/text.component.html.mustache +31 -0
  126. package/src/tasks/create/templates/SDK-Angular/field/Text-Input/text.component.scss.mustache +20 -0
  127. package/src/tasks/create/templates/SDK-Angular/field/Text-Input/text.component.spec.ts.mustache +25 -0
  128. package/src/tasks/create/templates/SDK-Angular/field/Text-Input/text.component.ts.mustache +177 -0
  129. package/src/tasks/create/templates/SDK-Angular/field/Text-Paragraph/config.json.mustache +83 -0
  130. package/src/tasks/create/templates/SDK-Angular/field/Text-Paragraph/text-paragraph.component.html.mustache +31 -0
  131. package/src/tasks/create/templates/SDK-Angular/field/Text-Paragraph/text-paragraph.component.scss.mustache +20 -0
  132. package/src/tasks/create/templates/SDK-Angular/field/Text-Paragraph/text-paragraph.component.spec.ts.mustache +25 -0
  133. package/src/tasks/create/templates/SDK-Angular/field/Text-Paragraph/text-paragraph.component.ts.mustache +179 -0
  134. package/src/tasks/create/templates/SDK-Angular/field/Text-Phone/config.json.mustache +95 -0
  135. package/src/tasks/create/templates/SDK-Angular/field/Text-Phone/text-phone.component.html.mustache +30 -0
  136. package/src/tasks/create/templates/SDK-Angular/field/Text-Phone/text-phone.component.scss.mustache +81 -0
  137. package/src/tasks/create/templates/SDK-Angular/field/Text-Phone/text-phone.component.spec.ts.mustache +25 -0
  138. package/src/tasks/create/templates/SDK-Angular/field/Text-Phone/text-phone.component.ts.mustache +194 -0
  139. package/src/tasks/create/templates/SDK-Angular/field/Text-URL/config.json.mustache +111 -0
  140. package/src/tasks/create/templates/SDK-Angular/field/Text-URL/text-url.component.html.mustache +30 -0
  141. package/src/tasks/create/templates/SDK-Angular/field/Text-URL/text-url.component.scss.mustache +20 -0
  142. package/src/tasks/create/templates/SDK-Angular/field/Text-URL/text-url.component.spec.ts.mustache +26 -0
  143. package/src/tasks/create/templates/SDK-Angular/field/Text-URL/text-url.component.ts.mustache +174 -0
  144. package/src/tasks/create/templates/SDK-Angular/field/TimeOfDay/time-of-day.component.html.mustache +30 -0
  145. package/src/tasks/create/templates/SDK-Angular/field/TimeOfDay/time-of-day.component.scss.mustache +20 -0
  146. package/src/tasks/create/templates/SDK-Angular/field/TimeOfDay/time-of-day.component.spec.ts.mustache +25 -0
  147. package/src/tasks/create/templates/SDK-Angular/field/TimeOfDay/time-of-day.component.ts.mustache +172 -0
  148. package/src/tasks/create/templates/SDK-Angular/template/DETAILS/DefaultForm.svg +29 -0
  149. package/src/tasks/create/templates/SDK-Angular/template/DETAILS/config.json.mustache +21 -0
  150. package/src/tasks/create/templates/SDK-Angular/template/DETAILS/demo.stories.jsx.mustache +71 -0
  151. package/src/tasks/create/templates/SDK-Angular/template/DETAILS/details.component.html.mustache +7 -0
  152. package/src/tasks/create/templates/SDK-Angular/template/DETAILS/details.component.scss.mustache +7 -0
  153. package/src/tasks/create/templates/SDK-Angular/template/DETAILS/details.component.spec.ts.mustache +25 -0
  154. package/src/tasks/create/templates/SDK-Angular/template/DETAILS/details.component.ts.mustache +121 -0
  155. package/src/tasks/create/templates/SDK-Angular/template/DETAILS/mock.stories.js.mustache +294 -0
  156. package/src/tasks/create/templates/SDK-Angular/template/DETAILS-region/TwoColumnDetails.svg +28 -0
  157. package/src/tasks/create/templates/SDK-Angular/template/DETAILS-region/config.json.mustache +27 -0
  158. package/src/tasks/create/templates/SDK-Angular/template/DETAILS-region/demo.stories.jsx.mustache +73 -0
  159. package/src/tasks/create/templates/SDK-Angular/template/DETAILS-region/details.component.html.mustache +6 -0
  160. package/src/tasks/create/templates/SDK-Angular/template/DETAILS-region/details.component.scss.mustache +7 -0
  161. package/src/tasks/create/templates/SDK-Angular/template/DETAILS-region/details.component.spec.ts.mustache +25 -0
  162. package/src/tasks/create/templates/SDK-Angular/template/DETAILS-region/details.component.ts.mustache +92 -0
  163. package/src/tasks/create/templates/SDK-Angular/template/DETAILS-region/mock.stories.js.mustache +290 -0
  164. package/src/tasks/create/templates/SDK-Angular/template/FORM/DefaultForm.svg +29 -0
  165. package/src/tasks/create/templates/SDK-Angular/template/FORM/config.json.mustache +45 -0
  166. package/src/tasks/create/templates/SDK-Angular/template/FORM/demo.stories.jsx.mustache +215 -0
  167. package/src/tasks/create/templates/SDK-Angular/template/FORM/form.component.html.mustache +23 -0
  168. package/src/tasks/create/templates/SDK-Angular/template/FORM/form.component.scss.mustache +33 -0
  169. package/src/tasks/create/templates/SDK-Angular/template/FORM/form.component.spec.ts.mustache +25 -0
  170. package/src/tasks/create/templates/SDK-Angular/template/FORM/form.component.ts.mustache +71 -0
  171. package/src/tasks/create/templates/SDK-Angular/template/FORM-region/TwoColumn.svg +38 -0
  172. package/src/tasks/create/templates/SDK-Angular/template/FORM-region/config.json.mustache +49 -0
  173. package/src/tasks/create/templates/SDK-Angular/template/FORM-region/demo.stories.jsx.mustache +183 -0
  174. package/src/tasks/create/templates/SDK-Angular/template/FORM-region/form.component.html.mustache +16 -0
  175. package/src/tasks/create/templates/SDK-Angular/template/FORM-region/form.component.scss.mustache +28 -0
  176. package/src/tasks/create/templates/SDK-Angular/template/FORM-region/form.component.spec.ts.mustache +25 -0
  177. package/src/tasks/create/templates/SDK-Angular/template/FORM-region/form.component.ts.mustache +26 -0
  178. package/src/tasks/create/templates/SDK-Angular/template/FORM-region/mock.stories.js.mustache +132 -0
  179. package/src/tasks/create/templates/SDK-Angular/template/PAGE/OneColumnPage.svg +28 -0
  180. package/src/tasks/create/templates/SDK-Angular/template/PAGE/config.json.mustache +32 -0
  181. package/src/tasks/create/templates/SDK-Angular/template/PAGE/demo.stories.jsx.mustache +80 -0
  182. package/src/tasks/create/templates/SDK-Angular/template/PAGE/mock.stories.js.mustache +115 -0
  183. package/src/tasks/create/templates/SDK-Angular/template/PAGE/page.component.html.mustache +15 -0
  184. package/src/tasks/create/templates/SDK-Angular/template/PAGE/page.component.scss.mustache +3 -0
  185. package/src/tasks/create/templates/SDK-Angular/template/PAGE/page.component.spec.ts.mustache +25 -0
  186. package/src/tasks/create/templates/SDK-Angular/template/PAGE/page.component.ts.mustache +63 -0
  187. package/src/tasks/create/templates/SDK-Angular/widget/CASE/OneColumnPage.svg +36 -0
  188. package/src/tasks/create/templates/SDK-Angular/widget/CASE/case.component.html.mustache +15 -0
  189. package/src/tasks/create/templates/SDK-Angular/widget/CASE/case.component.scss.mustache +22 -0
  190. package/src/tasks/create/templates/SDK-Angular/widget/CASE/case.component.spec.ts.mustache +25 -0
  191. package/src/tasks/create/templates/SDK-Angular/widget/CASE/case.component.ts.mustache +111 -0
  192. package/src/tasks/create/templates/SDK-Angular/widget/CASE/config.json.mustache +23 -0
  193. package/src/tasks/create/templates/SDK-Angular/widget/CASE/demo.stories.jsx.mustache +70 -0
  194. package/src/tasks/create/templates/SDK-Angular/widget/CASE/mock.stories.js.mustache +390 -0
  195. package/src/tasks/create/templates/SDK-Angular/widget/PAGE/OneColumnPage.svg +36 -0
  196. package/src/tasks/create/templates/SDK-Angular/widget/PAGE/config.json.mustache +59 -0
  197. package/src/tasks/create/templates/SDK-Angular/widget/PAGE/demo.stories.jsx.mustache +29 -0
  198. package/src/tasks/create/templates/SDK-Angular/widget/PAGE/mock.stories.js.mustache +23 -0
  199. package/src/tasks/create/templates/SDK-Angular/widget/PAGE/page.component.html.mustache +17 -0
  200. package/src/tasks/create/templates/SDK-Angular/widget/PAGE/page.component.scss.mustache +19 -0
  201. package/src/tasks/create/templates/SDK-Angular/widget/PAGE/page.component.ts.mustache +32 -0
  202. package/src/tasks/create/templates/SDK-Angular/widget/PAGE/pagecomponent.spec.ts.mustache +25 -0
  203. package/src/tasks/create/templates/SDK-Angular/widget/PAGE-CASE/OneColumnPage.svg +36 -0
  204. package/src/tasks/create/templates/SDK-Angular/widget/PAGE-CASE/config.json.mustache +66 -0
  205. package/src/tasks/create/templates/SDK-Angular/widget/PAGE-CASE/demo.stories.jsx.mustache +52 -0
  206. package/src/tasks/create/templates/SDK-Angular/widget/PAGE-CASE/mock.stories.js.mustache +52 -0
  207. package/src/tasks/create/templates/SDK-Angular/widget/PAGE-CASE/page-case.component.html.mustache +28 -0
  208. package/src/tasks/create/templates/SDK-Angular/widget/PAGE-CASE/page-case.component.scss.mustache +137 -0
  209. package/src/tasks/create/templates/SDK-Angular/widget/PAGE-CASE/page-case.component.spec.ts.mustache +25 -0
  210. package/src/tasks/create/templates/SDK-Angular/widget/PAGE-CASE/page-case.component.ts.mustache +115 -0
  211. package/src/tasks/create/templates/SDK-React/field/Boolean/index.tsx.mustache +3 -3
  212. package/src/tasks/create/templates/SDK-React/field/Date/index.tsx.mustache +2 -2
  213. package/src/tasks/create/templates/SDK-React/field/DateTime/index.tsx.mustache +6 -7
  214. package/src/tasks/create/templates/SDK-React/field/Decimal/demo.stories.jsx.mustache +5 -1
  215. package/src/tasks/create/templates/SDK-React/field/Decimal/index.tsx.mustache +78 -28
  216. package/src/tasks/create/templates/SDK-React/field/Decimal-Currency/index.tsx.mustache +3 -3
  217. package/src/tasks/create/templates/SDK-React/field/Decimal-Percentage/index.tsx.mustache +2 -2
  218. package/src/tasks/create/templates/SDK-React/field/Integer/index.tsx.mustache +2 -2
  219. package/src/tasks/create/templates/SDK-React/field/Picklist/demo.stories.jsx.mustache +13 -0
  220. package/src/tasks/create/templates/SDK-React/field/Picklist/index.tsx.mustache +30 -10
  221. package/src/tasks/create/templates/SDK-React/field/Text/index.tsx.mustache +2 -2
  222. package/src/tasks/create/templates/SDK-React/field/Text-Email/index.tsx.mustache +2 -2
  223. package/src/tasks/create/templates/SDK-React/field/Text-Input/index.tsx.mustache +2 -2
  224. package/src/tasks/create/templates/SDK-React/field/Text-Paragraph/index.tsx.mustache +2 -2
  225. package/src/tasks/create/templates/SDK-React/field/Text-Phone/index.tsx.mustache +6 -5
  226. package/src/tasks/create/templates/SDK-React/field/Text-URL/index.tsx.mustache +11 -0
  227. package/src/tasks/create/templates/SDK-React/field/TimeOfDay/config.json.mustache +135 -0
  228. package/src/tasks/create/templates/SDK-React/field/{TimeofDay → TimeOfDay}/index.tsx.mustache +12 -1
  229. package/src/tasks/create/templates/SDK-React/template/DETAILS/index.tsx.mustache +7 -3
  230. package/src/tasks/create/templates/SDK-React/template/DETAILS-region/index.tsx.mustache +8 -4
  231. package/src/tasks/create/templates/SDK-React/template/FORM/index.tsx.mustache +30 -20
  232. package/src/tasks/create/templates/SDK-React/template/FORM-region/demo.stories.jsx.mustache +2 -2
  233. package/src/tasks/create/templates/SDK-React/template/PAGE/demo.stories.jsx.mustache +1 -0
  234. package/src/tasks/create/templates/SDK-React/widget/CASE/index.tsx.mustache +4 -3
  235. package/src/tasks/create/templates/SDK-React/widget/PAGE/index.tsx.mustache +7 -7
  236. package/src/tasks/create/templates/SDK-React/widget/PAGE-CASE/demo.stories.jsx.mustache +2 -10
  237. package/src/tasks/create/templates/SDK-React/widget/PAGE-CASE/index.tsx.mustache +138 -36
  238. package/src/tasks/create/templates/SDK-React/widget/PAGE-CASE/mock.stories.js.mustache +1 -1
  239. package/src/tasks/create-all/index.js +112 -56
  240. package/src/tasks/delete/helper.js +88 -24
  241. package/src/tasks/delete/index.js +17 -8
  242. package/src/tasks/delete-all/helper.js +88 -25
  243. package/src/tasks/delete-all/index.js +15 -6
  244. package/src/tasks/list/helper.js +2 -3
  245. package/src/tasks/list/index.js +14 -7
  246. package/src/tasks/map/map.js +212 -20
  247. package/src/tasks/map-all/index.js +19 -2
  248. package/src/tasks/override/index.js +140 -79
  249. package/src/tasks/override-all/index.js +54 -6
  250. package/src/tasks/publish/helper.js +94 -51
  251. package/src/tasks/publish/index.js +87 -34
  252. package/src/tasks/publish-all/helper.js +97 -58
  253. package/src/tasks/publish-all/index.js +127 -48
  254. package/src/tasks/rename/index.js +184 -111
  255. package/src/tasks/validator/index.js +31 -7
  256. package/src/util.js +187 -39
  257. package/src/tasks/create/templates/Constellation/field/TimeofDay/styles.js.mustache +0 -7
  258. /package/src/tasks/bundle/{builder → builderAngular}/index.js +0 -0
  259. /package/src/tasks/bundle/{builder → builderAngular}/modules.js +0 -0
  260. /package/src/tasks/create/templates/Constellation/field/{TimeofDay → TimeOfDay}/config.json.mustache +0 -0
  261. /package/src/tasks/create/templates/Constellation/field/{TimeofDay → TimeOfDay}/demo.stories.jsx.mustache +0 -0
  262. /package/src/tasks/create/templates/Constellation/field/{TimeofDay → TimeOfDay}/index.jsx.mustache +0 -0
  263. /package/src/tasks/create/templates/Constellation/field/{TimeofDay → TimeOfDay}/mock.stories.js.mustache +0 -0
  264. /package/src/tasks/create/templates/Constellation/field/{TimeofDay → TimeOfDay}/suggestions-handler.js +0 -0
  265. /package/src/tasks/create/templates/{SDK-React/field/TimeofDay → SDK-Angular/field/TimeOfDay}/config.json.mustache +0 -0
  266. /package/src/tasks/create/templates/SDK-React/field/{TimeofDay → TimeOfDay}/demo.stories.jsx.mustache +0 -0
  267. /package/src/tasks/create/templates/SDK-React/field/{TimeofDay → TimeOfDay}/mock.stories.js.mustache +0 -0
  268. /package/src/tasks/create/templates/SDK-React/field/{TimeofDay → TimeOfDay}/styles.js.mustache +0 -0
package/src/auth.js CHANGED
@@ -1,573 +1,791 @@
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
- constructor(ssKeyConfig) {
14
- if (typeof ssKeyConfig === "string") {
15
- this.ssKeyConfig = ssKeyConfig;
16
- this.#reloadConfig();
17
- } else {
18
- // object with config structure is passed in
19
- this.config = ssKeyConfig;
20
- }
21
- 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
2
+ #config = null;
25
3
 
26
- // Prefer webcrypto.subtle over regular subtle
27
- // this.subtle = this.isNode ? (crypto.webcrypto ? crypto.webcrypto.subtle : crypto.subtle) : window.crypto.subtle;
28
-
29
- // Prefer regular subtle over webcrypto.suble for now
30
- this.subtle = this.isNode ? (crypto.subtle ? crypto.subtle : crypto.webcrypto.subtle) : window.crypto.subtle;
4
+ constructor(ssKeyConfig) {
5
+ if (typeof ssKeyConfig === 'string') {
6
+ this.ssKeyConfig = ssKeyConfig;
7
+ this.#reloadConfig();
8
+ } else {
9
+ // object with config structure is passed in
10
+ this.#config = ssKeyConfig;
11
+ }
12
+ this.urlencoded = 'application/x-www-form-urlencoded';
13
+ this.isNode = typeof window === 'undefined';
14
+
15
+ // For isNode path the below attributes are initialized on first method invocation
16
+ if (!this.isNode) {
17
+ this.crypto = window.crypto;
18
+ this.subtle = window.crypto.subtle;
19
+ }
20
+ if (Object.keys(this.#config).length > 0 && !this.#config.serverType) {
21
+ this.#config.serverType = 'infinity';
22
+ }
31
23
  }
32
24
 
33
25
  #reloadConfig() {
34
- if (!this.ssKeyConfig) {
35
- return;
26
+ if (!this.ssKeyConfig) {
27
+ return;
28
+ }
29
+ const peConfig = window.sessionStorage.getItem(this.ssKeyConfig);
30
+ let obj = {};
31
+ if (peConfig) {
32
+ try {
33
+ obj = JSON.parse(peConfig);
34
+ } catch (e) {
35
+ try {
36
+ obj = JSON.parse(atob(peConfig));
37
+ } catch (err) {
38
+ obj = {};
39
+ }
36
40
  }
37
- const peConfig = window.sessionStorage.getItem(this.ssKeyConfig);
38
- let obj = {};
39
- if (peConfig) {
40
- try {
41
- obj = JSON.parse(peConfig);
42
- } catch (e) {
43
- try {
44
- obj = JSON.parse(this.#atob(peConfig));
45
- } catch (e) {
46
- obj = {};
47
- }
48
- }
49
- }
50
- this.config = peConfig ? obj : {};
41
+ }
42
+ this.#config = peConfig ? obj : {};
51
43
  }
52
44
 
53
45
  #updateConfig() {
54
- if (this.ssKeyConfig) {
55
- window.sessionStorage.setItem(this.ssKeyConfig, this.#btoa(JSON.stringify(this.config)));
56
- }
46
+ if (this.ssKeyConfig) {
47
+ const val = JSON.stringify(this.#config);
48
+ // transform must occur unless it is explicitly disabled
49
+ const transform = this.#config.transform !== false;
50
+ window.sessionStorage.setItem(this.ssKeyConfig, transform ? btoa(val) : val);
51
+ }
57
52
  }
58
53
 
59
- // For PKCE the authorize includes a code_challenge & code_challenge_method as well
60
- async #buildAuthorizeUrl(state) {
61
- const { serverType, clientId, redirectUri, authorizeUri, authService, sessionIndex, appAlias, userIdentifier, password, noPKCE } =
62
- this.config;
63
-
64
- if (!noPKCE) {
65
- // Generate random string of 64 chars for verifier. RFC 7636 says from 43-128 chars
66
- const buf = new Uint8Array(64);
67
- this.crypto.getRandomValues(buf);
68
- this.config.codeVerifier = this.#base64UrlSafeEncode(buf);
69
- }
54
+ async #importSingleLib(libName, libProp, bLoadAlways = false) {
55
+ // eslint-disable-next-line no-undef
56
+ if (!bLoadAlways && typeof (this.isNode ? global : window)[libProp] !== 'undefined') {
57
+ // eslint-disable-next-line no-undef
58
+ this[libProp] = (this.isNode ? global : window)[libProp];
59
+ return this[libProp];
60
+ }
61
+ // Needed to explicitly make import argument a string by using template literals to fix a compile
62
+ // error: Critical dependency: the request of a dependency is an expression
63
+ return import(`${libName}`)
64
+ .then((mod) => {
65
+ this[libProp] = mod.default;
66
+ })
67
+ .catch((e) => {
68
+ // eslint-disable-next-line no-console
69
+ console.error(`Library ${libName} failed to load. ${e}`);
70
+ throw e;
71
+ });
72
+ }
70
73
 
71
- // If sessionIndex exists then increment attempts count (we will stop sending session_index after two failures)
72
- if (sessionIndex) {
73
- this.config.sessionIndexAttempts += 1;
74
+ async #importNodeLibs() {
75
+ // Also current assumption is using Node 18 or better
76
+ // With 18.3 there is now a native fetch (but may want to force use of node-fetch)
77
+ const useNodeFetch = !!this.#config.useNodeFetch;
78
+
79
+ return Promise.all([
80
+ this.#importSingleLib('node-fetch', 'fetch', useNodeFetch),
81
+ this.#importSingleLib('open', 'open'),
82
+ this.#importSingleLib('node:crypto', 'crypto', true),
83
+ this.#importSingleLib('node:https', 'https'),
84
+ this.#importSingleLib('node:http', 'http'),
85
+ this.#importSingleLib('node:fs', 'fs')
86
+ ]).then(() => {
87
+ this.subtle = this.crypto?.subtle || this.crypto.webcrypto.subtle;
88
+ if ((typeof fetch === 'undefined' || useNodeFetch) && this.fetch) {
89
+ /* eslint-disable-next-line no-global-assign */
90
+ fetch = this.fetch;
74
91
  }
75
- // Persist codeVerifier in session storage so it survives the redirects that are to follow
76
- this.#updateConfig();
77
-
78
- if (!state) {
79
- // Calc random state variable
80
- const buf = new Uint8Array(32);
81
- this.crypto.getRandomValues(buf);
82
- state = this.#base64UrlSafeEncode(buf);
83
- }
84
-
85
- // Trim alias to include just the real alias piece
86
- const addtlScope = appAlias ? '+app.alias.' + appAlias.replace(/^app\//, '') : '';
87
- const scope = (serverType === 'infinity' ? `openid${addtlScope}` : 'user_info');
88
-
89
- // Add explicit creds if specified to try to avoid login popup
90
- const moreAuthArgs = (serverType === 'infinity' ?
91
- '&enable_psyncid=true' +
92
- (authService ? `&authentication_service=${encodeURIComponent(authService)}` : '') +
93
- (sessionIndex && this.config.sessionIndexAttempts < 3 ? `&session_index=${sessionIndex}` : '') +
94
- (userIdentifier
95
- ? `&UserIdentifier=${encodeURIComponent(userIdentifier)}${password ? `&Password=${encodeURIComponent(this.#atob(password))}` : ''}` : '')
96
- : '');
92
+ });
93
+ }
97
94
 
98
- let pkceArgs = '';
99
- if (!noPKCE) {
100
- const cc = await this.#getCodeChallenge(this.config.codeVerifier);
101
- pkceArgs = `&code_challenge=${cc}&code_challenge_method=S256`;
102
- }
103
- const authUrl = `${authorizeUri}?client_id=${clientId}&response_type=code&redirect_uri=${redirectUri}&scope=${scope}&state=${state}${pkceArgs}${moreAuthArgs}`;
104
- // console.log(`authUrl: ${authUrl}`);
105
- return authUrl;
95
+ // For PKCE the authorize includes a code_challenge & code_challenge_method as well
96
+ async #buildAuthorizeUrl(state) {
97
+ const {
98
+ serverType,
99
+ clientId,
100
+ redirectUri,
101
+ authorizeUri,
102
+ authService,
103
+ sessionIndex,
104
+ appAlias,
105
+ userIdentifier,
106
+ password,
107
+ noPKCE,
108
+ isolationId
109
+ } = this.#config;
110
+ const bInfinity = serverType === 'infinity';
111
+
112
+ if (!noPKCE) {
113
+ // Generate random string of 64 chars for verifier. RFC 7636 says from 43-128 chars
114
+ const buf = new Uint8Array(64);
115
+ this.crypto.getRandomValues(buf);
116
+ this.#config.codeVerifier = this.#base64UrlSafeEncode(buf);
117
+ }
118
+
119
+ // If sessionIndex exists then increment attempts count (we will stop sending session_index after two failures)
120
+ // With Infinity '24 we can now properly detect a invalid_session_index error, but can't for earlier versions
121
+ if (sessionIndex) {
122
+ this.#config.sessionIndexAttempts += 1;
123
+ }
124
+
125
+ // We use state to verify that the received code is for the right authorize transaction
126
+ // eslint-disable-next-line no-unneeded-ternary
127
+ this.#config.state = `${state ? state : ''}.${this.#getRandomString(32)}`;
128
+
129
+ // The same redirectUri needs to be provided to token endpoint, so save this away incase redirectUri is
130
+ // adjusted for next authorize
131
+ this.#config.acRedirectUri = redirectUri;
132
+
133
+ // Persist codeVerifier in session storage so it survives the redirects that are to follow
134
+ this.#updateConfig();
135
+
136
+ // Trim alias to include just the real alias piece
137
+ const addtlScope = appAlias ? `+app.alias.${appAlias.replace(/^app\//, '')}` : '';
138
+ const scope = bInfinity ? `openid${addtlScope}` : 'user_info';
139
+
140
+ // Add explicit creds if specified to try to avoid login popup
141
+ const authServiceArg = authService ? `&authentication_service=${encodeURIComponent(authService)}` : '';
142
+ const sessionIndexArg =
143
+ sessionIndex && this.#config.sessionIndexAttempts < 3 ? `&session_index=${sessionIndex}` : '';
144
+ const userIdentifierArg = userIdentifier ? `&UserIdentifier=${encodeURIComponent(userIdentifier)}` : '';
145
+ const passwordArg = password && userIdentifier ? `&Password=${encodeURIComponent(atob(password))}` : '';
146
+ const moreAuthArgs = bInfinity
147
+ ? `&enable_psyncId=true${authServiceArg}${sessionIndexArg}${userIdentifierArg}${passwordArg}`
148
+ : `&isolationID=${isolationId}`;
149
+
150
+ let pkceArgs = '';
151
+ if (!noPKCE) {
152
+ const cc = await this.#getCodeChallenge(this.#config.codeVerifier);
153
+ pkceArgs = `&code_challenge=${cc}&code_challenge_method=S256`;
154
+ }
155
+ return `${authorizeUri}?client_id=${clientId}&response_type=code&redirect_uri=${redirectUri}&scope=${scope}&state=${this.#config.state}${pkceArgs}${moreAuthArgs}`;
106
156
  }
107
157
 
108
158
  async login() {
109
- const { grantType } = this.config;
110
- if (grantType && grantType !== 'authCode') {
111
- return this.getToken();
112
- } else {
113
- return this.#authCodeStart();
114
- }
159
+ if (this.isNode && !this.crypto) {
160
+ // Deferring dynamic loading of node libraries til this first method to avoid doing this in constructor
161
+ await this.#importNodeLibs();
162
+ }
163
+ const { grantType, noPKCE } = this.#config;
164
+ if (grantType && grantType !== 'authCode') {
165
+ return this.getToken();
166
+ }
167
+ // Make sure browser in a secure context, else PKCE will fail
168
+ if (!this.isNode && !noPKCE && !window.isSecureContext) {
169
+ throw new Error(
170
+ `Authorization code grant flow failed due to insecure browser context at ${window.location.origin}. Use localhost or https.`
171
+ );
172
+ }
173
+ return this.#authCodeStart();
115
174
  }
116
175
 
117
176
  // authCode login issues the authorize endpoint transaction and deals with redirects
118
177
  async #authCodeStart() {
119
- const fnGetRedirectUriOrigin = () => {
120
- let redirectUri = this.config.redirectUri;
121
- let nRootOffset = redirectUri.indexOf("//");
122
- let nFirstPathOffset = nRootOffset !== -1 ? redirectUri.indexOf("/", nRootOffset + 2) : -1;
123
- return nFirstPathOffset !== -1 ? redirectUri.substring(0, nFirstPathOffset) : redirectUri;
178
+ const fnGetRedirectUriOrigin = () => {
179
+ const redirectUri = this.#config.redirectUri;
180
+ const nRootOffset = redirectUri.indexOf('//');
181
+ const nFirstPathOffset = nRootOffset !== -1 ? redirectUri.indexOf('/', nRootOffset + 2) : -1;
182
+ return nFirstPathOffset !== -1 ? redirectUri.substring(0, nFirstPathOffset) : redirectUri;
183
+ };
184
+
185
+ const redirectOrigin = fnGetRedirectUriOrigin();
186
+ const state = this.isNode ? '' : btoa(window.location.origin);
187
+
188
+ return new Promise((resolve, reject) => {
189
+ let theUrl = null; // holds the crafted authorize url
190
+
191
+ let myWindow = null; // popup or iframe
192
+ let elIframe = null;
193
+ let elCloseBtn = null;
194
+ const iframeTimeout = this.#config.silentTimeout !== undefined ? this.#config.silentTimeout : 5000;
195
+ let bWinIframe = true;
196
+ let tmrAuthComplete = null;
197
+ let checkWindowClosed = null;
198
+ let bDisablePromptNone = false;
199
+ const myWinOnLoad = () => {
200
+ try {
201
+ if (bWinIframe) {
202
+ elIframe.contentWindow.postMessage({ type: 'PegaAuth' }, redirectOrigin);
203
+ } else {
204
+ myWindow.postMessage({ type: 'PegaAuth' }, redirectOrigin);
205
+ }
206
+ } catch (e) {
207
+ // Exception trying to postMessage on load (perhaps should console.warn)
208
+ }
209
+ };
210
+ const fnSetSilentAuthFailed = (bSet) => {
211
+ this.#config.silentAuthFailed = bSet;
212
+ this.#updateConfig();
213
+ };
214
+ /* eslint-disable prefer-promise-reject-errors */
215
+ const fnOpenPopup = () => {
216
+ if (this.#config.noPopups) {
217
+ return reject('no-popups');
218
+ }
219
+ // Since displaying a visible window, clear the silent auth failed flag
220
+ fnSetSilentAuthFailed(false);
221
+ myWindow = (this.isNode ? this.open : window.open)(theUrl, '_blank', 'width=700,height=500,left=200,top=100');
222
+ if (!myWindow) {
223
+ // Blocked by popup-blocker
224
+ return reject('blocked');
225
+ }
226
+ checkWindowClosed = setInterval(() => {
227
+ if (myWindow.closed) {
228
+ clearInterval(checkWindowClosed);
229
+ reject('closed');
230
+ }
231
+ }, 500);
232
+ if (!this.isNode) {
233
+ try {
234
+ myWindow.addEventListener('load', myWinOnLoad, true);
235
+ } catch (e) {
236
+ // Exception trying to add onload handler to opened window
237
+ // eslint-disable-next-line no-console
238
+ console.error(`Error adding event listener on popup window: ${e}`);
239
+ }
240
+ }
241
+ };
242
+
243
+ /* eslint-enable prefer-promise-reject-errors */
244
+ const fnCloseIframe = () => {
245
+ elIframe.parentNode.removeChild(elIframe);
246
+ elCloseBtn.parentNode.removeChild(elCloseBtn);
247
+ elIframe = null;
248
+ elCloseBtn = null;
249
+ bWinIframe = false;
250
+ };
251
+
252
+ const fnCloseAndReject = () => {
253
+ fnCloseIframe();
254
+ /* eslint-disable-next-line prefer-promise-reject-errors */
255
+ reject('closed');
256
+ };
257
+
258
+ const fnAuthMessageReceiver = (event) => {
259
+ // Check origin to make sure it is the redirect origin
260
+ if (event.origin !== redirectOrigin) return;
261
+ if (!event.data || !event.data.type || event.data.type !== 'PegaAuth') return;
262
+ const aArgs = ['code', 'state', 'error', 'errorDesc'];
263
+ const aValues = [];
264
+ for (let i = 0; i < aArgs.length; i += 1) {
265
+ const arg = aArgs[i];
266
+ aValues[arg] = event.data[arg] ? event.data[arg].toString() : null;
267
+ }
268
+ if (aValues.error || (aValues.code && aValues.state === this.#config.state)) {
269
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define
270
+ fnGetTokenAndFinish(aValues.code, aValues.error, aValues.errorDesc);
271
+ }
272
+ };
273
+
274
+ const fnEnableMessageReceiver = (bEnable) => {
275
+ if (bEnable) {
276
+ window.addEventListener('message', fnAuthMessageReceiver, false);
277
+ window.authCodeCallback = (code, state1, error, errorDesc) => {
278
+ if (error || (code && state1 === this.#config.state)) {
279
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define
280
+ fnGetTokenAndFinish(code, error, errorDesc);
281
+ }
282
+ };
283
+ } else {
284
+ window.removeEventListener('message', fnAuthMessageReceiver, false);
285
+ delete window.authCodeCallback;
286
+ }
124
287
  };
125
288
 
126
- const redirectOrigin = fnGetRedirectUriOrigin();
127
- const state = this.isNode ? '' : this.#btoa(location.origin);
128
-
129
- return new Promise((resolve, reject) => {
130
- /* eslint-disable consistent-return */
131
- this.#buildAuthorizeUrl(state).then((url) => {
132
- let myWindow = null; // popup or iframe
133
- let elIframe = null,
134
- elCloseBtn = null;
135
- const iframeTimeout = this.config.silentTimeout !== undefined ? this.config.silentTimeout : 5000;
136
- let bWinIframe = !this.isNode &&
137
- iframeTimeout > 0 &&
138
- ((!!this.config.userIdentifier && !!this.config.password) ||
139
- this.config.iframeLoginUI ||
140
- this.config.authService !== 'pega');
141
- let tmrAuthComplete = null,
142
- checkWindowClosed = null;
143
- const myWinOnLoad = () => {
144
- try {
145
- if (bWinIframe) {
146
- elIframe.contentWindow.postMessage({ type: "PegaAuth" }, redirectOrigin);
147
- console.log("authjs(login): loaded a page in iFrame");
148
- } else {
149
- myWindow.postMessage({ type: "PegaAuth" }, redirectOrigin);
150
- }
151
- } catch (e) {
152
- console.log("authjs(login): Exception trying to postMessage on load");
153
- }
154
- };
155
- const fnOpenPopup = () => {
156
- myWindow = (this.isNode ? open : window.open)(url, '_blank', 'width=700,height=500,left=200,top=100');
157
- if (!myWindow) {
158
- // Blocked by popup-blocker
159
- return reject("blocked");
160
- }
161
- checkWindowClosed = setInterval(() => {
162
- if (myWindow.closed) {
163
- clearInterval(checkWindowClosed);
164
- reject("closed");
165
- }
166
- }, 500);
167
- if (!this.isNode) {
168
- try {
169
- myWindow.addEventListener("load", myWinOnLoad, true);
170
- } catch (e) {
171
- console.log("authjs(login): Exception trying to add onload handler to opened window;");
172
- }
173
- }
174
- };
175
- const fnCloseIframe = () => {
176
- elIframe.parentNode.removeChild(elIframe);
177
- elCloseBtn.parentNode.removeChild(elCloseBtn);
178
- elIframe = elCloseBtn = null;
179
- bWinIframe = false;
180
- };
181
- const fnCloseAndReject = () => {
182
- fnCloseIframe();
183
- reject("closed");
184
- };
185
- let authMessageReceiver = null;
186
- /* Retrieve token(s) and close login window */
187
- const fnGetTokenAndFinish = (code) => {
188
- if (!this.isNode) {
189
- window.removeEventListener("message", authMessageReceiver, false);
190
- }
191
- this.getToken(code).then(token => {
192
- if (bWinIframe) {
193
- clearTimeout(tmrAuthComplete);
194
- fnCloseIframe();
195
- } else {
196
- if (!this.isNode) {
197
- clearInterval(checkWindowClosed);
198
- myWindow.close();
199
- }
200
- }
201
- resolve(token);
202
- })
203
- .catch(e => {
204
- reject(e);
205
- });
206
- };
207
- // If there is a userIdentifier and password specified or an external SSO auth service,
208
- // we can try to use this silently in an iFrame first
209
- if (bWinIframe) {
210
- const nFrameZLevel = 99999;
211
- elIframe = document.createElement('iframe');
212
- elIframe.id = 'pe' + this.config.clientId;
213
- const loginBoxWidth = 500,
214
- loginBoxHeight = 700;
215
- const oStyle = elIframe.style;
216
- oStyle.position = 'absolute';
217
- oStyle.display = 'none';
218
- oStyle.zIndex = nFrameZLevel;
219
- oStyle.top = `${Math.round(Math.max(window.innerHeight - loginBoxHeight, 0) / 2)}px`;
220
- oStyle.left = `${Math.round(Math.max(window.innerWidth - loginBoxWidth, 0) / 2)}px`;
221
- oStyle.width = '500px';
222
- oStyle.height = '700px';
223
- // Add Iframe to top of document DOM to have it load
224
- document.body.insertBefore(elIframe, document.body.firstChild);
225
- // document.getElementsByTagName('body')[0].appendChild(elIframe);
226
- elIframe.addEventListener('load', myWinOnLoad, true);
227
- // Disallow iframe content attempts to navigate main window
228
- elIframe.setAttribute('sandbox', 'allow-scripts allow-forms allow-same-origin');
229
- elIframe.setAttribute('src', url);
230
-
231
- const svgCloseBtn = `<?xml version="1.0" encoding="UTF-8"?>
232
- <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">
233
- <title>Dismiss - Black</title>
234
- <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
235
- <g transform="translate(1.000000, 1.000000)">
236
- <circle fill="#252C32" cx="16" cy="16" r="16"></circle>
237
- <g transform="translate(9.109375, 9.214844)" fill="#FFFFFF" fill-rule="nonzero">
238
- <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>
289
+ const doAuthorize = () => {
290
+ // If there is a userIdentifier and password specified or an external SSO auth service,
291
+ // we can try to use this silently in an iFrame first
292
+ bWinIframe =
293
+ !this.isNode &&
294
+ !this.#config.silentAuthFailed &&
295
+ iframeTimeout > 0 &&
296
+ ((!!this.#config.userIdentifier && !!this.#config.password) ||
297
+ this.#config.iframeLoginUI ||
298
+ this.#config.authService !== 'pega');
299
+ // Enable message receiver
300
+ if (!this.isNode) {
301
+ fnEnableMessageReceiver(true);
302
+ }
303
+ if (bWinIframe) {
304
+ const nFrameZLevel = 99999;
305
+ elIframe = document.createElement('iframe');
306
+ elIframe.id = `pe${this.#config.clientId}`;
307
+ const loginBoxWidth = 500;
308
+ const loginBoxHeight = 700;
309
+ const oStyle = elIframe.style;
310
+ oStyle.position = 'absolute';
311
+ oStyle.display = 'none';
312
+ oStyle.zIndex = nFrameZLevel;
313
+ oStyle.top = `${Math.round(Math.max(window.innerHeight - loginBoxHeight, 0) / 2)}px`;
314
+ oStyle.left = `${Math.round(Math.max(window.innerWidth - loginBoxWidth, 0) / 2)}px`;
315
+ oStyle.width = '500px';
316
+ oStyle.height = '700px';
317
+ // Add Iframe to top of document DOM to have it load
318
+ document.body.insertBefore(elIframe, document.body.firstChild);
319
+ // document.getElementsByTagName('body')[0].appendChild(elIframe);
320
+ elIframe.addEventListener('load', myWinOnLoad, true);
321
+ // Disallow iframe content attempts to navigate main window
322
+ elIframe.setAttribute('sandbox', 'allow-scripts allow-forms allow-same-origin');
323
+ // Adding prompt=none as this is standard OIDC way to communicate no UI is expected (expecting Pega security to support this one day)
324
+ elIframe.setAttribute('src', bDisablePromptNone ? theUrl : `${theUrl}&prompt=none`);
325
+
326
+ const svgCloseBtn = `<?xml version="1.0" encoding="UTF-8"?>
327
+ <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">
328
+ <title>Dismiss - Black</title>
329
+ <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
330
+ <g transform="translate(1.000000, 1.000000)">
331
+ <circle fill="#252C32" cx="16" cy="16" r="16"></circle>
332
+ <g transform="translate(9.109375, 9.214844)" fill="#FFFFFF" fill-rule="nonzero">
333
+ <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>
334
+ </g>
335
+ </g>
239
336
  </g>
240
- </g>
241
- </g>
242
- </svg>`;
243
- const bCloseWithinFrame = false;
244
- elCloseBtn = document.createElement('img');
245
- elCloseBtn.onclick = fnCloseAndReject;
246
- elCloseBtn.src = `data:image/svg+xml;base64,${this.#btoa(svgCloseBtn)}`;
247
- const oBtnStyle = elCloseBtn.style;
248
- oBtnStyle.cursor = 'pointer';
249
- // If svg doesn't set width and height might want to set oBtStyle width and height to something like '2em'
250
- oBtnStyle.position = 'absolute';
251
- oBtnStyle.display = 'none';
252
- oBtnStyle.zIndex = nFrameZLevel + 1;
253
- const nTopOffset = bCloseWithinFrame ? 5 : -10;
254
- const nRightOffset = bCloseWithinFrame ? -34 : -20;
255
- oBtnStyle.top = `${Math.round(Math.max(window.innerHeight - loginBoxHeight, 0) / 2) + nTopOffset}px`;
256
- oBtnStyle.left = `${Math.round(Math.max(window.innerWidth - loginBoxWidth, 0) / 2) + loginBoxWidth + nRightOffset
257
- }px`;
258
- document.body.insertBefore(elCloseBtn, document.body.firstChild);
259
- // If the password was wrong, then the login screen will be in the iframe
260
- // ..and with Pega without realization of US-372314 it may replace the top (main portal) window
261
- // For now set a timer and if the timer expires, remove the iFrame and use same url within
262
- // visible window
263
- tmrAuthComplete = setTimeout(() => {
264
- clearTimeout(tmrAuthComplete);
265
- // remove password from config
266
- if (this.config.password) {
267
- delete this.config.password;
268
- this.#updateConfig();
269
- }
270
- // Display the iframe where the redirects did not succeed (or invoke a popup window)
271
- if (this.config.iframeLoginUI) {
272
- elIframe.style.display = 'block';
273
- elCloseBtn.style.display = 'block';
274
- } else {
275
- fnCloseIframe();
276
- fnOpenPopup();
277
- }
278
- }, iframeTimeout);
279
- } else {
280
- if (this.isNode) {
281
- // Determine port to listen to by extracting it from redirect uri
282
- const { redirectUri, cert, key } = this.config;
283
- const isHttp = redirectUri.startsWith('http:');
284
- const nLocalhost = redirectUri.indexOf("localhost:");
285
- const nSlash = redirectUri.indexOf('/', nLocalhost + 10);
286
- const nPort = parseInt(redirectUri.substring(nLocalhost + 10, nSlash));
287
- if (-1 !== nLocalhost) {
288
- const options = key && cert && !isHttp ? {
289
- key: fs.readFileSync(key),
290
- cert: fs.readFileSync(cert)
291
- } : {};
292
- const server = (isHttp ? http : https).createServer(options, (req, res) => {
293
- const { method, url } = req;
294
- const { winTitle, winBodyHtml } = this.config;
295
- // console.log(`HTTP request received: ${url}`);
296
- res.writeHead(200, { 'Content-Type': 'text/html' });
297
- // Auto closing window for now. Can always leave it up and allow authConfig props to set title and bodyHtml
298
- res.end(`<html><head><title>${winTitle}</title><script>window.close();</script></head><body>${winBodyHtml}</body></html>`);
299
- const queryString = url.split('?')[1];
300
- const urlParams = new URLSearchParams(queryString);
301
- const code = urlParams.get('code');
302
- if (code) {
303
- // console.log(`Auth code received: ${code}`);
304
- // Stop receiving connections and close when all are handled.
305
- server.close();
306
- fnGetTokenAndFinish(code);
307
- }
308
- });
309
- server.listen(nPort);
310
- }
311
- }
312
- fnOpenPopup();
313
- }
314
-
315
- if (!this.isNode) {
316
- /* Handler to receive the auth code */
317
- authMessageReceiver = (event) => {
318
- // Check origin to make sure it is the redirect origin
319
- if (event.origin !== redirectOrigin) return;
320
- if (!event.data || !event.data.type || event.data.type !== 'PegaAuth') return;
321
- console.log('authjs(login): postMessage received with code');
322
- const code = event.data.code.toString();
323
- fnGetTokenAndFinish(code);
324
- };
325
- window.addEventListener('message', authMessageReceiver, false);
326
- window.authCodeCallback = function (code) {
327
- console.log('authjs(login): authCodeCallback used with code');
328
- fnGetTokenAndFinish(code);
329
- };
330
- }
331
- });
337
+ </svg>`;
338
+ const bCloseWithinFrame = false;
339
+ elCloseBtn = document.createElement('img');
340
+ elCloseBtn.onclick = fnCloseAndReject;
341
+ elCloseBtn.src = `data:image/svg+xml;base64,${btoa(svgCloseBtn)}`;
342
+ const oBtnStyle = elCloseBtn.style;
343
+ oBtnStyle.cursor = 'pointer';
344
+ // If svg doesn't set width and height might want to set oBtStyle width and height to something like '2em'
345
+ oBtnStyle.position = 'absolute';
346
+ oBtnStyle.display = 'none';
347
+ oBtnStyle.zIndex = nFrameZLevel + 1;
348
+ const nTopOffset = bCloseWithinFrame ? 5 : -10;
349
+ const nRightOffset = bCloseWithinFrame ? -34 : -20;
350
+ const nTop = Math.round(Math.max(window.innerHeight - loginBoxHeight, 0) / 2) + nTopOffset;
351
+ oBtnStyle.top = `${nTop}px`;
352
+ const nLeft = Math.round(Math.max(window.innerWidth - loginBoxWidth, 0) / 2) + loginBoxWidth + nRightOffset;
353
+ oBtnStyle.left = `${nLeft}px`;
354
+ document.body.insertBefore(elCloseBtn, document.body.firstChild);
355
+ // If the password was wrong, then the login screen will be in the iframe
356
+ // ..and with Pega without realization of US-372314 it may replace the top (main portal) window
357
+ // For now set a timer and if the timer expires, remove the iFrame and use same url within
358
+ // visible window
359
+ tmrAuthComplete = setTimeout(() => {
360
+ clearTimeout(tmrAuthComplete);
361
+ /*
362
+ // remove password from config
363
+ if (this.#config.password) {
364
+ delete this.#config.password;
365
+ this.#updateConfig();
366
+ }
367
+ */
368
+ // Display the iframe where the redirects did not succeed (or invoke a popup window)
369
+ if (this.#config.iframeLoginUI) {
370
+ elIframe.style.display = 'block';
371
+ elCloseBtn.style.display = 'block';
372
+ } else {
373
+ fnCloseIframe();
374
+ fnOpenPopup();
375
+ }
376
+ }, iframeTimeout);
377
+ } else {
378
+ if (this.isNode) {
379
+ // Determine port to listen to by extracting it from redirect uri
380
+ const { redirectUri, cert, key } = this.#config;
381
+ const isHttp = redirectUri.startsWith('http:');
382
+ const nLocalhost = redirectUri.indexOf('localhost:');
383
+ const nSlash = redirectUri.indexOf('/', nLocalhost + 10);
384
+ const nPort = parseInt(redirectUri.substring(nLocalhost + 10, nSlash), 10);
385
+ if (nLocalhost !== -1) {
386
+ const options =
387
+ key && cert && !isHttp
388
+ ? {
389
+ key: this.fs.readFileSync(key),
390
+ cert: this.fs.readFileSync(cert)
391
+ }
392
+ : {};
393
+ const server = (isHttp ? this.http : this.https).createServer(options, (req, res) => {
394
+ const { winTitle, winBodyHtml } = this.#config;
395
+ res.writeHead(200, { 'Content-Type': 'text/html' });
396
+ // Auto closing window for now. Can always leave it up and allow authConfig props to set title and bodyHtml
397
+ res.end(
398
+ `<html><head><title>${winTitle}</title><script>window.close();</script></head><body>${winBodyHtml}</body></html>`
399
+ );
400
+ const queryString = req.url.split('?')[1];
401
+ const urlParams = new URLSearchParams(queryString);
402
+ const code = urlParams.get('code');
403
+ const state1 = urlParams.get('state');
404
+ const error = urlParams.get('error');
405
+ const errorDesc = urlParams.get('error_description');
406
+ if (error || (code && state1 === this.#config.state)) {
407
+ // Stop receiving connections and close when all are handled.
408
+ server.close();
409
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define
410
+ fnGetTokenAndFinish(code, error, errorDesc);
411
+ }
412
+ });
413
+ /* eslint-enable no-undef */
414
+ server.listen(nPort);
415
+ }
416
+ }
417
+ fnOpenPopup();
418
+ }
419
+ };
420
+
421
+ /* Retrieve token(s) and close login window */
422
+ const fnGetTokenAndFinish = (code, error, errorDesc) => {
423
+ // Can clear state in session info at this point
424
+ delete this.#config.state;
425
+ this.#updateConfig();
426
+
427
+ if (!this.isNode) {
428
+ fnEnableMessageReceiver(false);
429
+ if (bWinIframe) {
430
+ clearTimeout(tmrAuthComplete);
431
+ fnCloseIframe();
432
+ } else {
433
+ clearInterval(checkWindowClosed);
434
+ myWindow.close();
435
+ }
436
+ }
437
+ if (code) {
438
+ this.getToken(code)
439
+ .then((token) => {
440
+ resolve(token);
441
+ })
442
+ .catch((e) => {
443
+ reject(e);
444
+ });
445
+ } else if (error) {
446
+ // Handle some errors in a special manner and pass others back to client
447
+ if (error === 'login_required') {
448
+ // eslint-disable-next-line no-console
449
+ console.warn('silent authentication failed...starting full authentication');
450
+ const bSpecialDebugPath = false;
451
+ if (bSpecialDebugPath) {
452
+ fnSetSilentAuthFailed(false);
453
+ bDisablePromptNone = true;
454
+ } else {
455
+ fnSetSilentAuthFailed(true);
456
+ bDisablePromptNone = false;
457
+ }
458
+ this.#buildAuthorizeUrl(state).then((url) => {
459
+ theUrl = url;
460
+ doAuthorize();
461
+ });
462
+ } else if (error === 'invalid_session_index') {
463
+ // eslint-disable-next-line no-console
464
+ console.warn('auth session no longer valid...starting new session');
465
+ // In these scenarios, not much user can do without just starting a new session, so do that
466
+ this.#updateSessionIndex(null);
467
+ fnSetSilentAuthFailed(false);
468
+ this.#buildAuthorizeUrl(state).then((url) => {
469
+ theUrl = url;
470
+ doAuthorize();
471
+ });
472
+ } else {
473
+ // eslint-disable-next-line no-console
474
+ console.warn(`Authorize failed: ${error}. ${errorDesc}\nFailing authorize url: ${theUrl}`);
475
+ throw new Error(error, { cause: errorDesc });
476
+ }
477
+ }
478
+ };
479
+
480
+ this.#buildAuthorizeUrl(state).then((url) => {
481
+ theUrl = url;
482
+ doAuthorize();
332
483
  });
484
+ });
333
485
  }
334
486
 
335
- // For PKCE token endpoint includes code_verifier
336
- getToken(authCode) {
337
- // Reload config to pick up the previously stored codeVerifier
338
- this.#reloadConfig();
339
-
340
- const { serverType, isolationId, clientId, clientSecret, redirectUri, tokenUri, codeVerifier, grantType,
341
- sessionIndex, customTokenParams, userIdentifier, password, noPKCE, ignoreInvalidCerts } = this.config;
487
+ // Login redirect
488
+ loginRedirect() {
489
+ // eslint-disable-next-line no-restricted-globals
490
+ const state = btoa(location.origin);
491
+ this.#buildAuthorizeUrl(state).then((url) => {
492
+ // eslint-disable-next-line no-restricted-globals
493
+ location.href = url;
494
+ });
495
+ }
342
496
 
343
- const bAuthCode = !grantType || grantType === 'authCode';
344
- if (bAuthCode && !authCode && !this.isNode) {
345
- const queryString = window.location.search;
346
- const urlParams = new URLSearchParams(queryString);
347
- authCode = urlParams.get('code');
348
- }
497
+ // Clear session index within config
498
+ #updateSessionIndex(sessionIndex) {
499
+ if (sessionIndex) {
500
+ this.#config.sessionIndex = sessionIndex;
501
+ this.#config.sessionIndexAttempts = 0;
502
+ } else if (this.#config.sessionIndex) {
503
+ delete this.#config.sessionIndex;
504
+ }
505
+ this.#updateConfig();
506
+ }
349
507
 
350
- const formData = new URLSearchParams();
351
- formData.append('client_id', clientId);
352
- if (clientSecret) {
353
- formData.append('client_secret', clientSecret);
354
- }
355
- const fullGTName = {
356
- "authCode": "authorization_code",
357
- "clientCreds": "client_credentials",
358
- "customBearer": "custom-bearer",
359
- "passwordCreds": "password"
360
- }[grantType];
361
- const grant_type = fullGTName ? fullGTName :
362
- (grantType ? grantType : 'authorization_code');
363
- formData.append('grant_type', grant_type);
364
- if (serverType === 'launchpad' && grantType !== 'authCode') {
365
- formData.append('isolation_ids', isolationId);
508
+ // For PKCE token endpoint includes code_verifier
509
+ getToken(authCode) {
510
+ // Reload config to pick up the previously stored codeVerifier
511
+ this.#reloadConfig();
512
+
513
+ const {
514
+ serverType,
515
+ isolationId,
516
+ clientId,
517
+ clientSecret,
518
+ acRedirectUri,
519
+ tokenUri,
520
+ codeVerifier,
521
+ grantType,
522
+ sessionIndex,
523
+ customTokenParams,
524
+ userIdentifier,
525
+ password,
526
+ noPKCE
527
+ } = this.#config;
528
+
529
+ const bAuthCode = !grantType || grantType === 'authCode';
530
+ if (bAuthCode && !authCode && !this.isNode) {
531
+ const queryString = window.location.search;
532
+ const urlParams = new URLSearchParams(queryString);
533
+ authCode = urlParams.get('code');
534
+ }
535
+
536
+ const formData = new URLSearchParams();
537
+ formData.append('client_id', clientId);
538
+ if (clientSecret) {
539
+ formData.append('client_secret', clientSecret);
540
+ }
541
+ /* eslint-disable camelcase */
542
+ const fullGTName = {
543
+ authCode: 'authorization_code',
544
+ clientCreds: 'client_credentials',
545
+ customBearer: 'custom-bearer',
546
+ passwordCreds: 'password'
547
+ }[grantType];
548
+ const grant_type = fullGTName || grantType || 'authorization_code';
549
+ formData.append('grant_type', grant_type);
550
+ if (serverType === 'launchpad' && grantType !== 'authCode') {
551
+ formData.append('isolation_ids', isolationId);
552
+ }
553
+ if (bAuthCode) {
554
+ formData.append('code', authCode);
555
+ formData.append('redirect_uri', acRedirectUri);
556
+ if (!noPKCE) {
557
+ formData.append('code_verifier', codeVerifier);
366
558
  }
367
- if (bAuthCode) {
368
- formData.append('code', authCode);
369
- formData.append('redirect_uri', redirectUri);
370
- if (!noPKCE) {
371
- formData.append('code_verifier', codeVerifier);
559
+ } else if (sessionIndex) {
560
+ formData.append('session_index', sessionIndex);
561
+ }
562
+ /* eslint-enable camelcase */
563
+ if (grantType === 'customBearer' && customTokenParams) {
564
+ Object.keys(customTokenParams).forEach((param) => {
565
+ formData.append(param, customTokenParams[param]);
566
+ });
567
+ }
568
+ if (grantType !== 'authCode') {
569
+ formData.append('enable_psyncId', 'true');
570
+ }
571
+ if (grantType === 'passwordCreds') {
572
+ formData.append('username', userIdentifier);
573
+ formData.append('password', atob(password));
574
+ }
575
+
576
+ return fetch(tokenUri, {
577
+ agent: this.#getAgent(),
578
+ method: 'POST',
579
+ headers: new Headers({
580
+ 'content-type': this.urlencoded
581
+ }),
582
+
583
+ body: formData.toString()
584
+ })
585
+ .then((response) => response.json())
586
+ .then((token) => {
587
+ if (token.errors || token.error) {
588
+ // eslint-disable-next-line no-console
589
+ console.error(`Token endpoint error: ${JSON.stringify(token.errors || token.error)}`);
590
+ } else {
591
+ // .expires_in contains the # of seconds before access token expires
592
+ // add property to keep track of current time when the token expires
593
+ token.eA = Date.now() + token.expires_in * 1000;
594
+ // Clear authCode related config state: state, codeVerifier, acRedirectUri
595
+ if (this.#config.state) {
596
+ delete this.#config.state;
372
597
  }
373
- } else if (sessionIndex) {
374
- formData.append('session_index', sessionIndex);
375
- }
376
- if (grantType === 'customBearer' && customTokenParams) {
377
- Object.keys(customTokenParams).forEach((param) => {
378
- formData.append(param, customTokenParams[param]);
379
- });
380
- }
381
- if (grantType === 'passwordCreds') {
382
- formData.append('username', userIdentifier);
383
- formData.append('password', this.#atob(password));
384
- }
385
- // console.log(`getToken form data: ${formData.toString()}`);
386
-
387
- return fetch(tokenUri, {
388
- agent: this.isNode && ignoreInvalidCerts ? new https.Agent({ rejectUnauthorized: false }) : undefined,
389
- method: 'POST',
390
- headers: new Headers({
391
- 'content-type': 'application/x-www-form-urlencoded'
392
- }),
393
-
394
- body: formData.toString()
598
+ if (this.#config.codeVerifier) {
599
+ delete this.#config.codeVerifier;
600
+ }
601
+ if (this.#config.acRedirectUri) {
602
+ delete this.#config.acRedirectUri;
603
+ }
604
+ // If there is a session_index then move this to the peConfig structure (as used on authorize)
605
+ if (token.session_index) {
606
+ this.#config.sessionIndex = token.session_index;
607
+ }
608
+ // If we got a token and have a session index, then reset the sessionIndexAttempts
609
+ if (this.#config.sessionIndex) {
610
+ this.#config.sessionIndexAttempts = 0;
611
+ }
612
+ this.#updateConfig();
613
+ }
614
+ return token;
395
615
  })
396
- .then((response) => response.json())
397
- .then((token) => {
398
- if (token.errors || token.error) {
399
- // console.log(`formData: ${formData.toString()}`);
400
- // console.log(`Token endpoint error: ${JSON.stringify(token.errors ? token.errors : token)}`);
401
- } else {
402
- // .expires_in contains the # of seconds before access token expires
403
- // add property to keep track of current time when the token expires
404
- /* eslint-disable camelcase */
405
- token.eA = Date.now() + token.expires_in * 1000;
406
- if (this.config.codeVerifier) {
407
- delete this.config.codeVerifier;
408
- }
409
- // If there is a session_index then move this to the peConfig structure (as used on authorize)
410
- if (token.session_index) {
411
- this.config.sessionIndex = token.session_index;
412
- }
413
- // If we got a token and have a session index, then reset the sessionIndexAttempts
414
- if (this.config.sessionIndex) {
415
- this.config.sessionIndexAttempts = 0;
416
- }
417
- this.#updateConfig();
418
- }
419
- return token;
420
- /* eslint-enable camelcase */
421
- })
422
- .catch((e) => {
423
- console.log(e);
424
- });
616
+ .catch((e) => {
617
+ // eslint-disable-next-line no-console
618
+ console.error(`Token endpoint error: ${e}`);
619
+ });
425
620
  }
426
621
 
427
622
  /* eslint-disable camelcase */
428
623
  async refreshToken(refresh_token) {
429
- const { clientId, clientSecret, tokenUri, ignoreInvalidCerts } = this.config;
430
-
431
- const formData = new URLSearchParams();
432
- formData.append('client_id', clientId);
433
- if (clientSecret) {
434
- formData.append('client_secret', clientSecret);
435
- }
436
- formData.append('grant_type', 'refresh_token');
437
- formData.append('refresh_token', refresh_token);
438
-
439
- return fetch(tokenUri, {
440
- agent: this.isNode && ignoreInvalidCerts ? new https.Agent({ rejectUnauthorized: false }) : undefined,
441
- method: 'POST',
442
- headers: new Headers({
443
- 'content-type': 'application/x-www-form-urlencoded'
444
- }),
445
-
446
- body: formData.toString()
624
+ const { clientId, clientSecret, tokenUri } = this.#config;
625
+
626
+ if (this.isNode && !this.crypto) {
627
+ // Deferring dynamic loading of node libraries til this first method to avoid doing this in constructor
628
+ await this.#importNodeLibs();
629
+ }
630
+
631
+ if (!refresh_token) {
632
+ return null;
633
+ }
634
+
635
+ const formData = new URLSearchParams();
636
+ formData.append('client_id', clientId);
637
+ if (clientSecret) {
638
+ formData.append('client_secret', clientSecret);
639
+ }
640
+ formData.append('grant_type', 'refresh_token');
641
+ formData.append('refresh_token', refresh_token);
642
+
643
+ return fetch(tokenUri, {
644
+ agent: this.#getAgent(),
645
+ method: 'POST',
646
+ headers: new Headers({
647
+ 'content-type': this.urlencoded
648
+ }),
649
+
650
+ body: formData.toString()
651
+ })
652
+ .then((response) => {
653
+ if (!response.ok && response.status === 401) {
654
+ return null;
655
+ }
656
+ return response.json();
447
657
  })
448
- .then((response) => {
449
- if (!response.ok && 401 === response.status) {
450
- return null;
451
- }
452
- return response.json();
453
- })
454
- .then((token) => {
455
- if (token) {
456
- // .expires_in contains the # of seconds before access token expires
457
- // add property to keep track of current time when the token expires
458
- token.eA = Date.now() + token.expires_in * 1000;
459
- }
460
- return token;
461
- })
462
- .catch((e) => {
463
- console.log(e);
464
- });
658
+ .then((token) => {
659
+ if (token) {
660
+ // .expires_in contains the # of seconds before access token expires
661
+ // add property to keep track of current time when the token expires
662
+ token.eA = Date.now() + token.expires_in * 1000;
663
+ }
664
+ return token;
665
+ })
666
+ .catch((e) => {
667
+ // eslint-disable-next-line no-console
668
+ console.warn(`Refresh token failed: ${e}`);
669
+ return null;
670
+ });
465
671
  }
466
672
 
467
673
  async revokeTokens(access_token, refresh_token = null) {
468
- if (Object.keys(this.config).length === 0) {
469
- // Must have a config structure to proceed
470
- return;
471
- }
472
- const { clientId, clientSecret, revokeUri, ignoreInvalidCerts } = this.config;
473
-
474
- let hdrs = { 'content-type': 'application/x-www-form-urlencoded' };
475
- if (clientSecret) {
476
- hdrs['authorization'] = 'Basic ' + this.#btoa(clientId + ':' + clientSecret);
477
- }
478
- let aTknProps = ['access_token'];
479
- if (refresh_token) {
480
- aTknProps.push('refresh_token');
674
+ if (Object.keys(this.#config).length === 0) {
675
+ // Must have a config structure to proceed
676
+ return;
677
+ }
678
+ const { clientId, clientSecret, revokeUri } = this.#config;
679
+
680
+ if (this.isNode && !this.crypto) {
681
+ // Deferring dynamic loading of node libraries til this first method to avoid doing this in constructor
682
+ await this.#importNodeLibs();
683
+ }
684
+
685
+ const hdrs = { 'content-type': this.urlencoded };
686
+ if (clientSecret) {
687
+ const basicCreds = btoa(`${clientId}:${clientSecret}`);
688
+ hdrs.authorization = `Basic ${basicCreds}`;
689
+ }
690
+ const aTknProps = ['access_token'];
691
+ if (refresh_token) {
692
+ aTknProps.push('refresh_token');
693
+ }
694
+ aTknProps.forEach((prop) => {
695
+ const formData = new URLSearchParams();
696
+ if (!clientSecret) {
697
+ formData.append('client_id', clientId);
481
698
  }
482
- aTknProps.forEach((prop) => {
483
- const formData = new URLSearchParams();
484
- if (!clientSecret) {
485
- formData.append('client_id', clientId);
699
+ formData.append('token', prop === 'access_token' ? access_token : refresh_token);
700
+ formData.append('token_type_hint', prop);
701
+ fetch(revokeUri, {
702
+ agent: this.#getAgent(),
703
+ method: 'POST',
704
+ headers: new Headers(hdrs),
705
+ body: formData.toString()
706
+ })
707
+ .then((response) => {
708
+ if (!response.ok) {
709
+ // eslint-disable-next-line no-console
710
+ console.error(`Error revoking ${prop}:${response.status}`);
486
711
  }
487
- formData.append('token', prop === 'access_token' ? access_token : refresh_token);
488
- formData.append('token_type_hint', prop);
489
- fetch(revokeUri, {
490
- agent: this.isNode && ignoreInvalidCerts ? new https.Agent({ rejectUnauthorized: false }) : undefined,
491
- method: 'POST',
492
- headers: new Headers(hdrs),
493
- body: formData.toString()
494
- })
495
- .then((response) => {
496
- if (!response.ok) {
497
- console.log('Error revoking ' + prop + ':' + response.status);
498
- }
499
- })
500
- .catch((e) => {
501
- console.log(e);
502
- });
503
- });
504
- // Also clobber any sessionIndex
505
- if (this.config.sessionIndex) {
506
- delete this.config.sessionIndex;
507
- this.#updateConfig();
508
- }
712
+ })
713
+ .catch((e) => {
714
+ // eslint-disable-next-line no-console
715
+ console.error(`Error revoking ${prop}; ${e}`);
716
+ });
717
+ });
718
+ this.#config.silentAuthFailed = false;
719
+ // Also clobber any sessionIndex
720
+ this.#updateSessionIndex(null);
509
721
  }
510
722
  /* eslint-enable camelcase */
511
723
 
512
724
  #sha256Hash(str) {
513
- // Found that the Node implementation of subtle.digest is yielding incorrect results
514
- // so using a different set of apis to get expected results.
515
- if (this.isNode) {
516
- return new Promise((resolve) => {
517
- return resolve(this.crypto.createHash("sha256").update(str).digest());
518
- });
519
- } else {
520
- return this.subtle.digest('SHA-256', new TextEncoder().encode(str));
521
- }
522
- }
523
-
524
- /* btoa to work with node or browser - create B64 string*/
525
- #btoa(str) {
526
- if (this.isNode) {
527
- return Buffer.from(str, 'binary').toString('base64');
528
- } else {
529
- return window.btoa(str);
530
- }
531
- }
532
-
533
- /* atoa to work with node or browser - convert B64 to binary */
534
- #atob(str) {
535
- if (this.isNode) {
536
- return Buffer.from(str, 'base64').toString('binary');
537
- } else {
538
- return window.atob(str);
539
- }
725
+ // Found that the Node implementation of subtle.digest is yielding incorrect results
726
+ // so using a different set of apis to get expected results.
727
+ if (this.isNode) {
728
+ return new Promise((resolve) => {
729
+ resolve(this.crypto.createHash('sha256').update(str).digest());
730
+ });
731
+ }
732
+ return this.subtle.digest('SHA-256', new TextEncoder().encode(str));
540
733
  }
541
734
 
542
735
  // Base64 encode
543
736
  /* eslint-disable-next-line class-methods-use-this */
544
737
  #encode64(buff) {
545
- return this.#btoa(new Uint8Array(buff).reduce((s, b) => s + String.fromCharCode(b), ''));
738
+ return btoa(new Uint8Array(buff).reduce((s, b) => s + String.fromCharCode(b), ''));
546
739
  }
547
740
 
548
741
  /*
549
742
  * Base64 url safe encoding of an array
550
743
  */
551
744
  #base64UrlSafeEncode(buf) {
552
- return this.#encode64(buf).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
745
+ return this.#encode64(buf).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
746
+ }
747
+
748
+ /*
749
+ * Get Random string starting with buffer of specified size
750
+ */
751
+ #getRandomString(nSize) {
752
+ const buf = new Uint8Array(nSize);
753
+ this.crypto.getRandomValues(buf);
754
+ return this.#base64UrlSafeEncode(buf);
553
755
  }
554
756
 
555
757
  /* Calc code verifier if necessary
556
758
  */
557
759
  /* eslint-disable camelcase */
558
- #getCodeChallenge(code_verifier) {
559
- return this.#sha256Hash(code_verifier)
560
- .then((hashed) => {
561
- return this.#base64UrlSafeEncode(hashed);
562
- })
563
- .catch((error) => {
564
- console.log(error);
565
- })
566
- .finally(() => {
567
- return null;
568
- });
760
+ async #getCodeChallenge(code_verifier) {
761
+ return this.#sha256Hash(code_verifier)
762
+ .then((hashed) => {
763
+ return this.#base64UrlSafeEncode(hashed);
764
+ })
765
+ .catch((error) => {
766
+ // eslint-disable-next-line no-console
767
+ console.error(`Error calculation code challenge for PKCE: ${error}`);
768
+ })
769
+ .finally(() => {
770
+ return null;
771
+ });
569
772
  }
570
773
  /* eslint-enable camelcase */
571
- }
572
774
 
573
- export default PegaAuth;
775
+ /*
776
+ * Return agent value for POST commands
777
+ */
778
+ #getAgent() {
779
+ if (this.isNode && this.#config.ignoreInvalidCerts) {
780
+ const options = {rejectUnauthorized: false};
781
+ if (this.#config.legacyTLS) {
782
+ options.secureOptions = this.crypto.constants.SSL_OP_LEGACY_SERVER_CONNECT;
783
+ }
784
+ return new this.https.Agent(options);
785
+ } else {
786
+ return undefined;
787
+ }
788
+ }
789
+ }
790
+
791
+ export default PegaAuth;