keycloakify 10.1.0-rc.1 → 10.1.1

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 (205) hide show
  1. package/PUBLIC_URL.js +2 -2
  2. package/PUBLIC_URL.js.map +1 -1
  3. package/account/KcContext/kcContextMocks.js +3 -3
  4. package/account/KcContext/kcContextMocks.js.map +1 -1
  5. package/bin/193.index.js +198 -62
  6. package/bin/{365.index.js → 20.index.js} +302 -263
  7. package/bin/31.index.js +15 -23
  8. package/bin/{430.index.js → 33.index.js} +579 -4
  9. package/bin/{678.index.js → 36.index.js} +2 -577
  10. package/bin/{440.index.js → 499.index.js} +259 -181
  11. package/bin/526.index.js +12 -793
  12. package/bin/599.index.js +4 -1
  13. package/bin/780.index.js +1 -1
  14. package/bin/{525.index.js → 903.index.js} +4840 -97
  15. package/bin/932.index.js +115 -886
  16. package/bin/main.js +4 -4
  17. package/bin/shared/buildContext.d.ts +0 -2
  18. package/bin/shared/buildContext.js.map +1 -1
  19. package/bin/shared/constants.d.ts +5 -6
  20. package/bin/shared/constants.js +5 -6
  21. package/bin/shared/constants.js.map +1 -1
  22. package/bin/shared/copyKeycloakResourcesToPublic.d.ts +2 -4
  23. package/bin/shared/copyKeycloakResourcesToPublic.js.map +1 -1
  24. package/login/KcContext/KcContext.d.ts +9 -18
  25. package/login/KcContext/KcContext.js.map +1 -1
  26. package/login/KcContext/kcContextMocks.js +6 -10
  27. package/login/KcContext/kcContextMocks.js.map +1 -1
  28. package/login/Template.js +4 -59
  29. package/login/Template.js.map +1 -1
  30. package/login/Template.useStylesAndScripts.d.ts +17 -0
  31. package/login/Template.useStylesAndScripts.js +69 -0
  32. package/login/Template.useStylesAndScripts.js.map +1 -0
  33. package/login/pages/Login.js +1 -1
  34. package/login/pages/Login.js.map +1 -1
  35. package/login/pages/LoginIdpLinkConfirmOverride.js +0 -1
  36. package/login/pages/LoginIdpLinkConfirmOverride.js.map +1 -1
  37. package/login/pages/LoginPasskeysConditionalAuthenticate.js +6 -49
  38. package/login/pages/LoginPasskeysConditionalAuthenticate.js.map +1 -1
  39. package/login/pages/LoginPasskeysConditionalAuthenticate.useScript.d.ts +20 -0
  40. package/login/pages/LoginPasskeysConditionalAuthenticate.useScript.js +49 -0
  41. package/login/pages/LoginPasskeysConditionalAuthenticate.useScript.js.map +1 -0
  42. package/login/pages/LoginRecoveryAuthnCodeConfig.js +4 -126
  43. package/login/pages/LoginRecoveryAuthnCodeConfig.js.map +1 -1
  44. package/login/pages/LoginRecoveryAuthnCodeConfig.useScript.d.ts +9 -0
  45. package/login/pages/LoginRecoveryAuthnCodeConfig.useScript.js +133 -0
  46. package/login/pages/LoginRecoveryAuthnCodeConfig.useScript.js.map +1 -0
  47. package/login/pages/LoginUsername.js +1 -1
  48. package/login/pages/LoginUsername.js.map +1 -1
  49. package/login/pages/Register.js +6 -3
  50. package/login/pages/Register.js.map +1 -1
  51. package/login/pages/WebauthnAuthenticate.js +13 -116
  52. package/login/pages/WebauthnAuthenticate.js.map +1 -1
  53. package/login/pages/WebauthnAuthenticate.useScript.d.ts +21 -0
  54. package/login/pages/WebauthnAuthenticate.useScript.js +41 -0
  55. package/login/pages/WebauthnAuthenticate.useScript.js.map +1 -0
  56. package/login/pages/WebauthnRegister.js +8 -178
  57. package/login/pages/WebauthnRegister.js.map +1 -1
  58. package/login/pages/WebauthnRegister.useScript.d.ts +27 -0
  59. package/login/pages/WebauthnRegister.useScript.js +49 -0
  60. package/login/pages/WebauthnRegister.useScript.js.map +1 -0
  61. package/package.json +125 -15
  62. package/res/account-v1/account.ftl +70 -0
  63. package/res/account-v1/applications.ftl +76 -0
  64. package/res/account-v1/federatedIdentity.ftl +42 -0
  65. package/res/account-v1/log.ftl +35 -0
  66. package/res/account-v1/messages/messages_ar.properties +406 -0
  67. package/res/account-v1/messages/messages_ca.properties +147 -0
  68. package/res/account-v1/messages/messages_cs.properties +171 -0
  69. package/res/account-v1/messages/messages_da.properties +339 -0
  70. package/res/account-v1/messages/messages_de.properties +353 -0
  71. package/res/account-v1/messages/messages_en.properties +404 -0
  72. package/res/account-v1/messages/messages_es.properties +147 -0
  73. package/res/account-v1/messages/messages_fi.properties +400 -0
  74. package/res/account-v1/messages/messages_fr.properties +180 -0
  75. package/res/account-v1/messages/messages_hu.properties +334 -0
  76. package/res/account-v1/messages/messages_it.properties +336 -0
  77. package/res/account-v1/messages/messages_ja.properties +335 -0
  78. package/res/account-v1/messages/messages_lt.properties +154 -0
  79. package/res/account-v1/messages/messages_lv.properties +197 -0
  80. package/res/account-v1/messages/messages_nl.properties +371 -0
  81. package/res/account-v1/messages/messages_no.properties +152 -0
  82. package/res/account-v1/messages/messages_pl.properties +248 -0
  83. package/res/account-v1/messages/messages_pt_BR.properties +349 -0
  84. package/res/account-v1/messages/messages_ru.properties +235 -0
  85. package/res/account-v1/messages/messages_sk.properties +196 -0
  86. package/res/account-v1/messages/messages_sv.properties +150 -0
  87. package/res/account-v1/messages/messages_tr.properties +315 -0
  88. package/res/account-v1/messages/messages_zh_CN.properties +153 -0
  89. package/res/account-v1/password.ftl +59 -0
  90. package/res/account-v1/resource-detail.ftl +277 -0
  91. package/res/account-v1/resources/css/account.css +277 -0
  92. package/res/account-v1/resources/img/icon-sidebar-active.png +0 -0
  93. package/res/account-v1/resources/img/keycloak-logo.png +0 -0
  94. package/res/account-v1/resources/img/logo.png +0 -0
  95. package/res/account-v1/resources/resources-common/img/favicon.ico +0 -0
  96. package/res/account-v1/resources/resources-common/node_modules/patternfly/dist/css/patternfly-additions.min.css +5 -0
  97. package/res/account-v1/resources/resources-common/node_modules/patternfly/dist/css/patternfly.min.css +8 -0
  98. package/res/account-v1/resources/resources-common/node_modules/patternfly/dist/fonts/OpenSans-Bold-webfont.woff2 +0 -0
  99. package/res/account-v1/resources/resources-common/node_modules/patternfly/dist/fonts/OpenSans-Light-webfont.woff2 +0 -0
  100. package/res/account-v1/resources/resources-common/node_modules/patternfly/dist/fonts/OpenSans-Regular-webfont.woff2 +0 -0
  101. package/res/account-v1/resources/resources-common/node_modules/patternfly/dist/fonts/OpenSans-Semibold-webfont.woff2 +0 -0
  102. package/res/account-v1/resources/resources-common/node_modules/patternfly/dist/fonts/PatternFlyIcons-webfont.ttf +0 -0
  103. package/res/account-v1/resources/resources-common/node_modules/patternfly/dist/fonts/PatternFlyIcons-webfont.woff +0 -0
  104. package/res/account-v1/resources.ftl +403 -0
  105. package/res/account-v1/sessions.ftl +44 -0
  106. package/res/account-v1/template.ftl +88 -0
  107. package/res/account-v1/theme.properties +14 -0
  108. package/res/account-v1/totp.ftl +141 -0
  109. package/res/public/.keycloakify/account/css/account.css +277 -0
  110. package/res/public/.keycloakify/account/img/icon-sidebar-active.png +0 -0
  111. package/res/public/.keycloakify/account/img/keycloak-logo.png +0 -0
  112. package/res/public/.keycloakify/account/img/logo.png +0 -0
  113. package/res/public/.keycloakify/account/resources-common/img/favicon.ico +0 -0
  114. package/res/public/.keycloakify/account/resources-common/node_modules/patternfly/dist/css/patternfly-additions.min.css +5 -0
  115. package/res/public/.keycloakify/account/resources-common/node_modules/patternfly/dist/css/patternfly.min.css +8 -0
  116. package/res/public/.keycloakify/account/resources-common/node_modules/patternfly/dist/fonts/OpenSans-Bold-webfont.woff2 +0 -0
  117. package/res/public/.keycloakify/account/resources-common/node_modules/patternfly/dist/fonts/OpenSans-Light-webfont.woff2 +0 -0
  118. package/res/public/.keycloakify/account/resources-common/node_modules/patternfly/dist/fonts/OpenSans-Regular-webfont.woff2 +0 -0
  119. package/res/public/.keycloakify/account/resources-common/node_modules/patternfly/dist/fonts/OpenSans-Semibold-webfont.woff2 +0 -0
  120. package/res/public/.keycloakify/account/resources-common/node_modules/patternfly/dist/fonts/PatternFlyIcons-webfont.ttf +0 -0
  121. package/res/public/.keycloakify/account/resources-common/node_modules/patternfly/dist/fonts/PatternFlyIcons-webfont.woff +0 -0
  122. package/res/public/.keycloakify/login/css/login.css +629 -0
  123. package/res/public/.keycloakify/login/img/feedback-error-arrow-down.png +0 -0
  124. package/res/public/.keycloakify/login/img/feedback-error-sign.png +0 -0
  125. package/res/public/.keycloakify/login/img/feedback-success-arrow-down.png +0 -0
  126. package/res/public/.keycloakify/login/img/feedback-success-sign.png +0 -0
  127. package/res/public/.keycloakify/login/img/feedback-warning-arrow-down.png +0 -0
  128. package/res/public/.keycloakify/login/img/feedback-warning-sign.png +0 -0
  129. package/res/public/.keycloakify/login/img/keycloak-bg.png +0 -0
  130. package/res/public/.keycloakify/login/img/keycloak-logo-text.png +0 -0
  131. package/res/public/.keycloakify/login/img/keycloak-logo.png +0 -0
  132. package/res/public/.keycloakify/login/js/authChecker.js +49 -0
  133. package/res/public/.keycloakify/login/js/common.js +48 -0
  134. package/res/public/.keycloakify/login/js/kcMultivalued.js +106 -0
  135. package/res/public/.keycloakify/login/js/kcNumberFormat.js +21 -0
  136. package/res/public/.keycloakify/login/js/kcNumberUnFormat.js +19 -0
  137. package/res/public/.keycloakify/login/js/menu-button-links.js +315 -0
  138. package/{src/bin/shared/downloadKeycloakDefaultTheme/extra-assets → res/public/.keycloakify/login/js}/passkeysConditionalAuth.js +1 -1
  139. package/res/public/.keycloakify/login/js/passwordVisibility.js +15 -0
  140. package/res/public/.keycloakify/login/js/userProfile.js +71 -0
  141. package/res/public/.keycloakify/login/js/webauthnRegister.js +140 -0
  142. package/res/public/.keycloakify/login/resources-common/img/favicon.ico +0 -0
  143. package/res/public/.keycloakify/login/resources-common/lib/pficon/pficon.css +22 -0
  144. package/res/public/.keycloakify/login/resources-common/lib/pficon/pficon.woff2 +0 -0
  145. package/res/public/.keycloakify/login/resources-common/node_modules/@patternfly/patternfly/patternfly.min.css +2 -0
  146. package/res/public/.keycloakify/login/resources-common/node_modules/jquery/dist/jquery.min.js +2 -0
  147. package/res/public/.keycloakify/login/resources-common/node_modules/patternfly/dist/css/patternfly-additions.min.css +5 -0
  148. package/res/public/.keycloakify/login/resources-common/node_modules/patternfly/dist/css/patternfly.min.css +8 -0
  149. package/res/public/.keycloakify/login/resources-common/node_modules/patternfly/dist/fonts/OpenSans-Bold-webfont.ttf +0 -0
  150. package/res/public/.keycloakify/login/resources-common/node_modules/patternfly/dist/fonts/OpenSans-Bold-webfont.woff +0 -0
  151. package/res/public/.keycloakify/login/resources-common/node_modules/patternfly/dist/fonts/OpenSans-Bold-webfont.woff2 +0 -0
  152. package/res/public/.keycloakify/login/resources-common/node_modules/patternfly/dist/fonts/OpenSans-Light-webfont.woff2 +0 -0
  153. package/res/public/.keycloakify/login/resources-common/node_modules/patternfly/dist/fonts/OpenSans-Regular-webfont.woff2 +0 -0
  154. package/res/public/.keycloakify/login/resources-common/node_modules/patternfly/dist/fonts/OpenSans-Semibold-webfont.woff2 +0 -0
  155. package/res/public/.keycloakify/login/resources-common/node_modules/patternfly/dist/fonts/PatternFlyIcons-webfont.ttf +0 -0
  156. package/res/public/.keycloakify/login/resources-common/node_modules/patternfly/dist/fonts/PatternFlyIcons-webfont.woff +0 -0
  157. package/res/public/.keycloakify/login/resources-common/node_modules/patternfly/dist/fonts/fontawesome-webfont.woff2 +0 -0
  158. package/res/public/.keycloakify/login/resources-common/node_modules/patternfly/dist/img/bg-login.jpg +0 -0
  159. package/res/public/.keycloakify/login/resources-common/node_modules/rfc4648/lib/rfc4648.js +178 -0
  160. package/src/PUBLIC_URL.ts +5 -3
  161. package/src/account/KcContext/kcContextMocks.ts +3 -3
  162. package/src/bin/copy-keycloak-resources-to-public.ts +1 -1
  163. package/src/bin/initialize-account-theme/initializeAccountTheme_singlePage.ts +4 -1
  164. package/src/bin/initialize-email-theme.ts +28 -8
  165. package/src/bin/keycloakify/buildJars/buildJar.ts +3 -7
  166. package/src/bin/keycloakify/generateFtl/generateFtl.ts +3 -7
  167. package/src/bin/keycloakify/generateResources/generateResourcesForMainTheme.ts +65 -90
  168. package/src/bin/keycloakify/keycloakify.ts +38 -21
  169. package/src/bin/keycloakify/replacers/replaceImportsInCssCode.ts +2 -2
  170. package/src/bin/keycloakify/replacers/replaceImportsInJsCode/vite.ts +3 -3
  171. package/src/bin/keycloakify/replacers/replaceImportsInJsCode/webpack.ts +3 -3
  172. package/src/bin/shared/buildContext.ts +1 -8
  173. package/src/bin/shared/constants.ts +5 -7
  174. package/src/bin/shared/copyKeycloakResourcesToPublic.ts +30 -40
  175. package/src/bin/start-keycloak/start-keycloak.ts +10 -12
  176. package/src/login/KcContext/KcContext.ts +9 -18
  177. package/src/login/KcContext/kcContextMocks.ts +5 -13
  178. package/src/login/Template.tsx +4 -67
  179. package/src/login/Template.useStylesAndScripts.ts +94 -0
  180. package/src/login/pages/Login.tsx +1 -1
  181. package/src/login/pages/LoginIdpLinkConfirmOverride.tsx +0 -1
  182. package/src/login/pages/LoginPasskeysConditionalAuthenticate.tsx +6 -81
  183. package/src/login/pages/LoginPasskeysConditionalAuthenticate.useScript.tsx +72 -0
  184. package/src/login/pages/LoginRecoveryAuthnCodeConfig.tsx +4 -126
  185. package/src/login/pages/LoginRecoveryAuthnCodeConfig.useScript.tsx +142 -0
  186. package/src/login/pages/LoginUsername.tsx +1 -1
  187. package/src/login/pages/Register.tsx +35 -13
  188. package/src/login/pages/WebauthnAuthenticate.tsx +20 -133
  189. package/src/login/pages/WebauthnAuthenticate.useScript.tsx +64 -0
  190. package/src/login/pages/WebauthnRegister.tsx +8 -195
  191. package/src/login/pages/WebauthnRegister.useScript.tsx +93 -0
  192. package/src/tools/useInsertScriptTags.ts +14 -4
  193. package/src/vite-plugin/vite-plugin.ts +11 -15
  194. package/tools/useInsertScriptTags.d.ts +2 -2
  195. package/tools/useInsertScriptTags.js +8 -2
  196. package/tools/useInsertScriptTags.js.map +1 -1
  197. package/vite-plugin/index.js +3357 -47132
  198. package/bin/697.index.js +0 -4749
  199. package/bin/shared/downloadKeycloakStaticResources.d.ts +0 -9
  200. package/bin/shared/downloadKeycloakStaticResources.js.map +0 -1
  201. package/src/bin/keycloakify/generateResources/bringInAccountV1.ts +0 -89
  202. package/src/bin/shared/downloadKeycloakDefaultTheme/downloadKeycloakDefaultTheme.ts +0 -338
  203. package/src/bin/shared/downloadKeycloakDefaultTheme/index.ts +0 -1
  204. package/src/bin/shared/downloadKeycloakStaticResources.ts +0 -53
  205. /package/{src/bin/shared/downloadKeycloakDefaultTheme/extra-assets → res/public/.keycloakify/login/js}/webauthnAuthenticate.js +0 -0
@@ -1,6 +1,7 @@
1
1
  import { useState } from "react";
2
2
  import type { LazyOrNot } from "keycloakify/tools/LazyOrNot";
3
3
  import { getKcClsx, type KcClsx } from "keycloakify/login/lib/kcClsx";
4
+ import { clsx } from "keycloakify/tools/clsx";
4
5
  import type { UserProfileFormFieldsProps } from "keycloakify/login/UserProfileFormFieldsProps";
5
6
  import type { PageProps } from "keycloakify/login/pages/PageProps";
6
7
  import type { KcContext } from "../KcContext";
@@ -19,9 +20,10 @@ export default function Register(props: RegisterProps) {
19
20
  classes
20
21
  });
21
22
 
22
- const { url, messagesPerField, recaptchaRequired, recaptchaSiteKey, termsAcceptanceRequired } = kcContext;
23
+ const { messageHeader, url, messagesPerField, recaptchaRequired, recaptchaVisible, recaptchaSiteKey, recaptchaAction, termsAcceptanceRequired } =
24
+ kcContext;
23
25
 
24
- const { msg, msgStr } = i18n;
26
+ const { msg, msgStr, advancedMsg } = i18n;
25
27
 
26
28
  const [isFormSubmittable, setIsFormSubmittable] = useState(false);
27
29
  const [areTermsAccepted, setAreTermsAccepted] = useState(false);
@@ -32,7 +34,7 @@ export default function Register(props: RegisterProps) {
32
34
  i18n={i18n}
33
35
  doUseDefaultCss={doUseDefaultCss}
34
36
  classes={classes}
35
- headerNode={msg("registerTitle")}
37
+ headerNode={messageHeader !== undefined ? advancedMsg(messageHeader) : msg("registerTitle")}
36
38
  displayMessage={messagesPerField.exists("global")}
37
39
  displayRequiredFields
38
40
  >
@@ -53,10 +55,10 @@ export default function Register(props: RegisterProps) {
53
55
  onAreTermsAcceptedValueChange={setAreTermsAccepted}
54
56
  />
55
57
  )}
56
- {recaptchaRequired && (
58
+ {recaptchaRequired && (recaptchaVisible || recaptchaAction === undefined) && (
57
59
  <div className="form-group">
58
60
  <div className={kcClsx("kcInputWrapperClass")}>
59
- <div className="g-recaptcha" data-size="compact" data-sitekey={recaptchaSiteKey}></div>
61
+ <div className="g-recaptcha" data-size="compact" data-sitekey={recaptchaSiteKey} data-action={recaptchaAction}></div>
60
62
  </div>
61
63
  </div>
62
64
  )}
@@ -68,14 +70,34 @@ export default function Register(props: RegisterProps) {
68
70
  </span>
69
71
  </div>
70
72
  </div>
71
- <div id="kc-form-buttons" className={kcClsx("kcFormButtonsClass")}>
72
- <input
73
- disabled={!isFormSubmittable || (termsAcceptanceRequired && !areTermsAccepted)}
74
- className={kcClsx("kcButtonClass", "kcButtonPrimaryClass", "kcButtonBlockClass", "kcButtonLargeClass")}
75
- type="submit"
76
- value={msgStr("doRegister")}
77
- />
78
- </div>
73
+
74
+ {recaptchaRequired && !recaptchaVisible && recaptchaAction !== undefined ? (
75
+ <div id="kc-form-buttons" className={kcClsx("kcFormButtonsClass")}>
76
+ <button
77
+ className={clsx(
78
+ kcClsx("kcButtonClass", "kcButtonPrimaryClass", "kcButtonBlockClass", "kcButtonLargeClass"),
79
+ "g-recaptcha"
80
+ )}
81
+ data-sitekey={recaptchaSiteKey}
82
+ data-callback={() => {
83
+ (document.getElementById("kc-register-form") as HTMLFormElement).submit();
84
+ }}
85
+ data-action={recaptchaAction}
86
+ type="submit"
87
+ >
88
+ {msg("doRegister")}
89
+ </button>
90
+ </div>
91
+ ) : (
92
+ <div id="kc-form-buttons" className={kcClsx("kcFormButtonsClass")}>
93
+ <input
94
+ disabled={!isFormSubmittable || (termsAcceptanceRequired && !areTermsAccepted)}
95
+ className={kcClsx("kcButtonClass", "kcButtonPrimaryClass", "kcButtonBlockClass", "kcButtonLargeClass")}
96
+ type="submit"
97
+ value={msgStr("doRegister")}
98
+ />
99
+ </div>
100
+ )}
79
101
  </div>
80
102
  </form>
81
103
  </Template>
@@ -1,8 +1,7 @@
1
- import { useEffect, Fragment } from "react";
2
- import { assert } from "keycloakify/tools/assert";
1
+ import { Fragment } from "react";
3
2
  import { clsx } from "keycloakify/tools/clsx";
4
- import { useInsertScriptTags } from "keycloakify/tools/useInsertScriptTags";
5
3
  import { getKcClsx } from "keycloakify/login/lib/kcClsx";
4
+ import { useScript } from "keycloakify/login/pages/WebauthnAuthenticate.useScript";
6
5
  import type { PageProps } from "keycloakify/login/pages/PageProps";
7
6
  import type { KcContext } from "../KcContext";
8
7
  import type { I18n } from "../i18n";
@@ -12,136 +11,25 @@ export default function WebauthnAuthenticate(props: PageProps<Extract<KcContext,
12
11
 
13
12
  const { kcClsx } = getKcClsx({ doUseDefaultCss, classes });
14
13
 
15
- const {
16
- url,
17
- isUserIdentified,
18
- challenge,
19
- userVerification,
20
- rpId,
21
- createTimeout,
22
- messagesPerField,
23
- realm,
24
- registrationDisabled,
25
- authenticators,
26
- shouldDisplayAuthenticators
27
- } = kcContext;
14
+ const { url, realm, registrationDisabled, authenticators, shouldDisplayAuthenticators } = kcContext;
28
15
 
29
16
  const { msg, msgStr, advancedMsg } = i18n;
30
17
 
31
- const { insertScriptTags } = useInsertScriptTags({
32
- componentOrHookName: "WebauthnAuthenticate",
33
- scriptTags: [
34
- {
35
- type: "text/javascript",
36
- src: `${url.resourcesCommonPath}/node_modules/jquery/dist/jquery.min.js`
37
- },
38
- {
39
- type: "text/javascript",
40
- src: `${url.resourcesPath}/js/base64url.js`
41
- },
42
- {
43
- type: "text/javascript",
44
- textContent: `
18
+ const authButtonId = "authenticateWebAuthnButton";
45
19
 
46
- function webAuthnAuthenticate() {
47
- let isUserIdentified = ${isUserIdentified};
48
- if (!isUserIdentified) {
49
- doAuthenticate([]);
50
- return;
51
- }
52
- checkAllowCredentials();
53
- }
54
-
55
- function checkAllowCredentials() {
56
- let allowCredentials = [];
57
- let authn_use = document.forms['authn_select'].authn_use_chk;
58
-
59
- if (authn_use !== undefined) {
60
- if (authn_use.length === undefined) {
61
- allowCredentials.push({
62
- id: base64url.decode(authn_use.value, {loose: true}),
63
- type: 'public-key',
64
- });
65
- } else {
66
- for (let i = 0; i < authn_use.length; i++) {
67
- allowCredentials.push({
68
- id: base64url.decode(authn_use[i].value, {loose: true}),
69
- type: 'public-key',
70
- });
71
- }
72
- }
73
- }
74
- doAuthenticate(allowCredentials);
75
- }
76
-
77
-
78
- function doAuthenticate(allowCredentials) {
79
-
80
- // Check if WebAuthn is supported by this browser
81
- if (!window.PublicKeyCredential) {
82
- $("#error").val("${msgStr("webauthn-unsupported-browser-text")}");
83
- $("#webauth").submit();
84
- return;
85
- }
86
-
87
- let challenge = "${challenge}";
88
- let userVerification = "${userVerification}";
89
- let rpId = "${rpId}";
90
- let publicKey = {
91
- rpId : rpId,
92
- challenge: base64url.decode(challenge, { loose: true })
93
- };
94
-
95
- let createTimeout = ${createTimeout};
96
- if (createTimeout !== 0) publicKey.timeout = createTimeout * 1000;
97
-
98
- if (allowCredentials.length) {
99
- publicKey.allowCredentials = allowCredentials;
100
- }
101
-
102
- if (userVerification !== 'not specified') publicKey.userVerification = userVerification;
103
-
104
- navigator.credentials.get({publicKey})
105
- .then((result) => {
106
- window.result = result;
107
-
108
- let clientDataJSON = result.response.clientDataJSON;
109
- let authenticatorData = result.response.authenticatorData;
110
- let signature = result.response.signature;
111
-
112
- $("#clientDataJSON").val(base64url.encode(new Uint8Array(clientDataJSON), { pad: false }));
113
- $("#authenticatorData").val(base64url.encode(new Uint8Array(authenticatorData), { pad: false }));
114
- $("#signature").val(base64url.encode(new Uint8Array(signature), { pad: false }));
115
- $("#credentialId").val(result.id);
116
- if(result.response.userHandle) {
117
- $("#userHandle").val(base64url.encode(new Uint8Array(result.response.userHandle), { pad: false }));
118
- }
119
- $("#webauth").submit();
120
- })
121
- .catch((err) => {
122
- $("#error").val(err);
123
- $("#webauth").submit();
124
- })
125
- ;
126
- }
127
-
128
- `
129
- }
130
- ]
20
+ useScript({
21
+ authButtonId,
22
+ kcContext,
23
+ i18n
131
24
  });
132
25
 
133
- useEffect(() => {
134
- insertScriptTags();
135
- }, []);
136
-
137
26
  return (
138
27
  <Template
139
28
  kcContext={kcContext}
140
29
  i18n={i18n}
141
30
  doUseDefaultCss={doUseDefaultCss}
142
31
  classes={classes}
143
- displayMessage={!messagesPerField.existsError("username")}
144
- displayInfo={realm.password && realm.registrationAllowed && !registrationDisabled}
32
+ displayInfo={realm.registrationAllowed && !registrationDisabled}
145
33
  infoNode={
146
34
  <div id="kc-registration">
147
35
  <span>
@@ -179,7 +67,7 @@ export default function WebauthnAuthenticate(props: PageProps<Extract<KcContext,
179
67
  )}
180
68
  <div className={kcClsx("kcFormOptionsClass")}>
181
69
  {authenticators.authenticators.map((authenticator, i) => (
182
- <div key={i} id="kc-webauthn-authenticator" className={kcClsx("kcSelectAuthListItemClass")}>
70
+ <div key={i} id={`kc-webauthn-authenticator-item-${i}`} className={kcClsx("kcSelectAuthListItemClass")}>
183
71
  <div className={kcClsx("kcSelectAuthListItemIconClass")}>
184
72
  <i
185
73
  className={clsx(
@@ -195,12 +83,15 @@ export default function WebauthnAuthenticate(props: PageProps<Extract<KcContext,
195
83
  />
196
84
  </div>
197
85
  <div className={kcClsx("kcSelectAuthListItemArrowIconClass")}>
198
- <div id="kc-webauthn-authenticator-label" className={kcClsx("kcSelectAuthListItemHeadingClass")}>
86
+ <div
87
+ id={`kc-webauthn-authenticator-label-${i}`}
88
+ className={kcClsx("kcSelectAuthListItemHeadingClass")}
89
+ >
199
90
  {advancedMsg(authenticator.label)}
200
91
  </div>
201
92
  {authenticator.transports.displayNameProperties?.length && (
202
93
  <div
203
- id="kc-webauthn-authenticator-transport"
94
+ id={`kc-webauthn-authenticator-transport-${i}`}
204
95
  className={kcClsx("kcSelectAuthListItemDescriptionClass")}
205
96
  >
206
97
  {authenticator.transports.displayNameProperties
@@ -217,8 +108,10 @@ export default function WebauthnAuthenticate(props: PageProps<Extract<KcContext,
217
108
  </div>
218
109
  )}
219
110
  <div className={kcClsx("kcSelectAuthListItemDescriptionClass")}>
220
- <span id="kc-webauthn-authenticator-created-label">{msg("webauthn-createdAt-label")}</span>
221
- <span id="kc-webauthn-authenticator-created">{authenticator.createdAt}</span>
111
+ <span id={`kc-webauthn-authenticator-createdlabel-${i}`}>
112
+ {msg("webauthn-createdAt-label")}
113
+ </span>
114
+ <span id={`kc-webauthn-authenticator-created-${i}`}>{authenticator.createdAt}</span>
222
115
  </div>
223
116
  <div className={kcClsx("kcSelectAuthListItemFillClass")} />
224
117
  </div>
@@ -229,16 +122,10 @@ export default function WebauthnAuthenticate(props: PageProps<Extract<KcContext,
229
122
  )}
230
123
  </>
231
124
  )}
232
-
233
125
  <div id="kc-form-buttons" className={kcClsx("kcFormButtonsClass")}>
234
126
  <input
235
- id="authenticateWebAuthnButton"
127
+ id={authButtonId}
236
128
  type="button"
237
- onClick={() => {
238
- assert("webAuthnAuthenticate" in window);
239
- assert(typeof window.webAuthnAuthenticate === "function");
240
- window.webAuthnAuthenticate();
241
- }}
242
129
  autoFocus
243
130
  value={msgStr("webauthn-doAuthenticate")}
244
131
  className={kcClsx("kcButtonClass", "kcButtonPrimaryClass", "kcButtonBlockClass", "kcButtonLargeClass")}
@@ -0,0 +1,64 @@
1
+ import { useEffect } from "react";
2
+ import { useInsertScriptTags } from "keycloakify/tools/useInsertScriptTags";
3
+ import { assert } from "keycloakify/tools/assert";
4
+ import { KcContext } from "keycloakify/login/KcContext/KcContext";
5
+
6
+ type KcContextLike = {
7
+ url: {
8
+ resourcesPath: string;
9
+ };
10
+ isUserIdentified: "true" | "false";
11
+ challenge: string;
12
+ userVerification: KcContext.WebauthnAuthenticate["userVerification"];
13
+ rpId: string;
14
+ createTimeout: number | string;
15
+ };
16
+
17
+ assert<keyof KcContextLike extends keyof KcContext.WebauthnAuthenticate ? true : false>();
18
+ assert<KcContext.WebauthnAuthenticate extends KcContextLike ? true : false>();
19
+
20
+ type I18nLike = {
21
+ msgStr: (key: "webauthn-unsupported-browser-text") => string;
22
+ isFetchingTranslations: boolean;
23
+ };
24
+
25
+ export function useScript(params: { authButtonId: string; kcContext: KcContextLike; i18n: I18nLike }) {
26
+ const { authButtonId, kcContext, i18n } = params;
27
+
28
+ const { url, isUserIdentified, challenge, userVerification, rpId, createTimeout } = kcContext;
29
+
30
+ const { msgStr, isFetchingTranslations } = i18n;
31
+
32
+ const { insertScriptTags } = useInsertScriptTags({
33
+ componentOrHookName: "WebauthnAuthenticate",
34
+ scriptTags: [
35
+ {
36
+ type: "module",
37
+ textContent: () => `
38
+
39
+ import { authenticateByWebAuthn } from "${url.resourcesPath}/js/webauthnAuthenticate.js";
40
+ const authButton = document.getElementById('${authButtonId}');
41
+ authButton.addEventListener("click", function() {
42
+ const input = {
43
+ isUserIdentified : ${isUserIdentified},
44
+ challenge : '${challenge}',
45
+ userVerification : '${userVerification}',
46
+ rpId : '${rpId}',
47
+ createTimeout : ${createTimeout},
48
+ errmsg : ${JSON.stringify(msgStr("webauthn-unsupported-browser-text"))}
49
+ };
50
+ authenticateByWebAuthn(input);
51
+ });
52
+ `
53
+ }
54
+ ]
55
+ });
56
+
57
+ useEffect(() => {
58
+ if (isFetchingTranslations) {
59
+ return;
60
+ }
61
+
62
+ insertScriptTags();
63
+ }, [isFetchingTranslations]);
64
+ }
@@ -1,7 +1,5 @@
1
- import { useEffect } from "react";
2
- import { assert } from "keycloakify/tools/assert";
3
1
  import { getKcClsx, type KcClsx } from "keycloakify/login/lib/kcClsx";
4
- import { useInsertScriptTags } from "keycloakify/tools/useInsertScriptTags";
2
+ import { useScript } from "keycloakify/login/pages/WebauthnRegister.useScript";
5
3
  import type { PageProps } from "keycloakify/login/pages/PageProps";
6
4
  import type { KcContext } from "../KcContext";
7
5
  import type { I18n } from "../i18n";
@@ -11,198 +9,18 @@ export default function WebauthnRegister(props: PageProps<Extract<KcContext, { p
11
9
 
12
10
  const { kcClsx } = getKcClsx({ doUseDefaultCss, classes });
13
11
 
14
- const {
15
- url,
16
- challenge,
17
- userid,
18
- username,
19
- signatureAlgorithms,
20
- rpEntityName,
21
- rpId,
22
- attestationConveyancePreference,
23
- authenticatorAttachment,
24
- requireResidentKey,
25
- userVerificationRequirement,
26
- createTimeout,
27
- excludeCredentialIds,
28
- isSetRetry,
29
- isAppInitiatedAction
30
- } = kcContext;
12
+ const { url, isSetRetry, isAppInitiatedAction } = kcContext;
31
13
 
32
14
  const { msg, msgStr } = i18n;
33
15
 
34
- const { insertScriptTags } = useInsertScriptTags({
35
- componentOrHookName: "WebauthnRegister",
36
- scriptTags: [
37
- {
38
- type: "text/javascript",
39
- src: `${url.resourcesCommonPath}/node_modules/jquery/dist/jquery.min.js`
40
- },
41
- {
42
- type: "text/javascript",
43
- src: `${url.resourcesPath}/js/base64url.js`
44
- },
45
- {
46
- type: "text/javascript",
47
- textContent: `
48
- function registerSecurityKey() {
16
+ const authButtonId = "authenticateWebAuthnButton";
49
17
 
50
- // Check if WebAuthn is supported by this browser
51
- if (!window.PublicKeyCredential) {
52
- $("#error").val("${msgStr("webauthn-unsupported-browser-text")}");
53
- $("#register").submit();
54
- return;
55
- }
56
-
57
- // mandatory parameters
58
- let challenge = "${challenge}";
59
- let userid = "${userid}";
60
- let username = "${username}";
61
-
62
- let signatureAlgorithms =${JSON.stringify(signatureAlgorithms)};
63
- let pubKeyCredParams = getPubKeyCredParams(signatureAlgorithms);
64
-
65
- let rpEntityName = "${rpEntityName}";
66
- let rp = {name: rpEntityName};
67
-
68
- let publicKey = {
69
- challenge: base64url.decode(challenge, {loose: true}),
70
- rp: rp,
71
- user: {
72
- id: base64url.decode(userid, {loose: true}),
73
- name: username,
74
- displayName: username
75
- },
76
- pubKeyCredParams: pubKeyCredParams,
77
- };
78
-
79
- // optional parameters
80
- let rpId = "${rpId}";
81
- publicKey.rp.id = rpId;
82
-
83
- let attestationConveyancePreference = "${attestationConveyancePreference}";
84
- if (attestationConveyancePreference !== 'not specified') publicKey.attestation = attestationConveyancePreference;
85
-
86
- let authenticatorSelection = {};
87
- let isAuthenticatorSelectionSpecified = false;
88
-
89
- let authenticatorAttachment = "${authenticatorAttachment}";
90
- if (authenticatorAttachment !== 'not specified') {
91
- authenticatorSelection.authenticatorAttachment = authenticatorAttachment;
92
- isAuthenticatorSelectionSpecified = true;
93
- }
94
-
95
- let requireResidentKey = "${requireResidentKey}";
96
- if (requireResidentKey !== 'not specified') {
97
- if (requireResidentKey === 'Yes')
98
- authenticatorSelection.requireResidentKey = true;
99
- else
100
- authenticatorSelection.requireResidentKey = false;
101
- isAuthenticatorSelectionSpecified = true;
102
- }
103
-
104
- let userVerificationRequirement = "${userVerificationRequirement}";
105
- if (userVerificationRequirement !== 'not specified') {
106
- authenticatorSelection.userVerification = userVerificationRequirement;
107
- isAuthenticatorSelectionSpecified = true;
108
- }
109
-
110
- if (isAuthenticatorSelectionSpecified) publicKey.authenticatorSelection = authenticatorSelection;
111
-
112
- let createTimeout = ${createTimeout};
113
- if (createTimeout !== 0) publicKey.timeout = createTimeout * 1000;
114
-
115
- let excludeCredentialIds = "${excludeCredentialIds}";
116
- let excludeCredentials = getExcludeCredentials(excludeCredentialIds);
117
- if (excludeCredentials.length > 0) publicKey.excludeCredentials = excludeCredentials;
118
-
119
- navigator.credentials.create({publicKey})
120
- .then(function (result) {
121
- window.result = result;
122
- let clientDataJSON = result.response.clientDataJSON;
123
- let attestationObject = result.response.attestationObject;
124
- let publicKeyCredentialId = result.rawId;
125
-
126
- $("#clientDataJSON").val(base64url.encode(new Uint8Array(clientDataJSON), {pad: false}));
127
- $("#attestationObject").val(base64url.encode(new Uint8Array(attestationObject), {pad: false}));
128
- $("#publicKeyCredentialId").val(base64url.encode(new Uint8Array(publicKeyCredentialId), {pad: false}));
129
-
130
- if (typeof result.response.getTransports === "function") {
131
- let transports = result.response.getTransports();
132
- if (transports) {
133
- $("#transports").val(getTransportsAsString(transports));
134
- }
135
- } else {
136
- console.log("Your browser is not able to recognize supported transport media for the authenticator.");
137
- }
138
-
139
- let initLabel = "WebAuthn Authenticator (Default Label)";
140
- let labelResult = window.prompt("Please input your registered authenticator's label", initLabel);
141
- if (labelResult === null) labelResult = initLabel;
142
- $("#authenticatorLabel").val(labelResult);
143
-
144
- $("#register").submit();
145
-
146
- })
147
- .catch(function (err) {
148
- $("#error").val(err);
149
- $("#register").submit();
150
-
151
- });
152
- }
153
-
154
- function getPubKeyCredParams(signatureAlgorithmsList) {
155
- let pubKeyCredParams = [];
156
- if (signatureAlgorithmsList.length === 0) {
157
- pubKeyCredParams.push({type: "public-key", alg: -7});
158
- return pubKeyCredParams;
159
- }
160
-
161
- for (let i = 0; i < signatureAlgorithmsList.length; i++) {
162
- pubKeyCredParams.push({
163
- type: "public-key",
164
- alg: signatureAlgorithmsList[i]
165
- });
166
- }
167
- return pubKeyCredParams;
168
- }
169
-
170
- function getExcludeCredentials(excludeCredentialIds) {
171
- let excludeCredentials = [];
172
- if (excludeCredentialIds === "") return excludeCredentials;
173
-
174
- let excludeCredentialIdsList = excludeCredentialIds.split(',');
175
-
176
- for (let i = 0; i < excludeCredentialIdsList.length; i++) {
177
- excludeCredentials.push({
178
- type: "public-key",
179
- id: base64url.decode(excludeCredentialIdsList[i],
180
- {loose: true})
181
- });
182
- }
183
- return excludeCredentials;
184
- }
185
-
186
- function getTransportsAsString(transportsList) {
187
- if (transportsList === '' || Array.isArray(transportsList)) return "";
188
-
189
- let transportsString = "";
190
-
191
- for (let i = 0; i < transportsList.length; i++) {
192
- transportsString += transportsList[i] + ",";
193
- }
194
-
195
- return transportsString.slice(0, -1);
196
- }
197
- `
198
- }
199
- ]
18
+ useScript({
19
+ authButtonId,
20
+ kcContext,
21
+ i18n
200
22
  });
201
23
 
202
- useEffect(() => {
203
- insertScriptTags();
204
- }, []);
205
-
206
24
  return (
207
25
  <Template
208
26
  kcContext={kcContext}
@@ -230,13 +48,8 @@ export default function WebauthnRegister(props: PageProps<Extract<KcContext, { p
230
48
  <input
231
49
  type="submit"
232
50
  className={kcClsx("kcButtonClass", "kcButtonPrimaryClass", "kcButtonBlockClass", "kcButtonLargeClass")}
233
- id="registerWebAuthn"
51
+ id={authButtonId}
234
52
  value={msgStr("doRegisterSecurityKey")}
235
- onClick={() => {
236
- assert("registerSecurityKey" in window);
237
- assert(typeof window.registerSecurityKey === "function");
238
- window.registerSecurityKey();
239
- }}
240
53
  />
241
54
 
242
55
  {!isSetRetry && isAppInitiatedAction && (