From b4e7d9b1aa09da44ba673dcaf6658a2d71e11cdf Mon Sep 17 00:00:00 2001 From: Takashi Norimatsu Date: Thu, 16 May 2024 14:58:43 +0900 Subject: [PATCH] Passkeys: Supporting WebAuthn Conditional UI (#24305) closes #24264 Signed-off-by: Takashi Norimatsu Signed-off-by: mposolda Co-authored-by: mposolda --- .../java/org/keycloak/common/Profile.java | 3 + .../passkey-conditional-ui-authentication.png | Bin 0 -> 19236 bytes .../passkey-conditional-ui-autofill.png | Bin 0 -> 38060 bytes ...conditional-ui-fallback-authentication.png | Bin 0 -> 21896 bytes .../images/passkey-conditional-ui-flow.png | Bin 0 -> 15670 bytes .../topics/authentication/passkeys.adoc | 77 ++++++++++ .../PasskeysConditionalUIAuthenticator.java | 43 ++++++ ...keysConditionalUIAuthenticatorFactory.java | 63 ++++++++ ...ycloak.authentication.AuthenticatorFactory | 3 +- .../pages/WebAuthnAuthenticatorsList.java | 17 ++- .../webauthn/pages/WebAuthnLoginPage.java | 2 +- ...ogin-passkeys-conditional-authenticate.ftl | 143 ++++++++++++++++++ .../login/messages/messages_en.properties | 8 + .../resources/js/passkeysConditionalAuth.js | 79 ++++++++++ .../resources/js/webauthnAuthenticate.js | 4 +- .../base/login/webauthn-authenticate.ftl | 18 +-- 16 files changed, 441 insertions(+), 19 deletions(-) create mode 100644 docs/documentation/server_admin/images/passkey-conditional-ui-authentication.png create mode 100644 docs/documentation/server_admin/images/passkey-conditional-ui-autofill.png create mode 100644 docs/documentation/server_admin/images/passkey-conditional-ui-fallback-authentication.png create mode 100644 docs/documentation/server_admin/images/passkey-conditional-ui-flow.png create mode 100644 services/src/main/java/org/keycloak/authentication/authenticators/browser/PasskeysConditionalUIAuthenticator.java create mode 100644 services/src/main/java/org/keycloak/authentication/authenticators/browser/PasskeysConditionalUIAuthenticatorFactory.java create mode 100644 themes/src/main/resources/theme/base/login/login-passkeys-conditional-authenticate.ftl create mode 100644 themes/src/main/resources/theme/base/login/resources/js/passkeysConditionalAuth.js diff --git a/common/src/main/java/org/keycloak/common/Profile.java b/common/src/main/java/org/keycloak/common/Profile.java index 2061c6e7deb..fa307a0e987 100755 --- a/common/src/main/java/org/keycloak/common/Profile.java +++ b/common/src/main/java/org/keycloak/common/Profile.java @@ -111,7 +111,10 @@ public class Profile { OID4VC_VCI("Support for the OID4VCI protocol as part of OID4VC.", Type.EXPERIMENTAL), DECLARATIVE_UI("declarative ui spi", Type.EXPERIMENTAL), + ORGANIZATION("Organization support within realms", Type.EXPERIMENTAL), + + PASSKEYS("Passkeys", Type.PREVIEW) ; private final Type type; diff --git a/docs/documentation/server_admin/images/passkey-conditional-ui-authentication.png b/docs/documentation/server_admin/images/passkey-conditional-ui-authentication.png new file mode 100644 index 0000000000000000000000000000000000000000..41046a819568a438161e58b52b4f97f274506aed GIT binary patch literal 19236 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 A!vFvP literal 0 HcmV?d00001 diff --git a/docs/documentation/server_admin/images/passkey-conditional-ui-autofill.png b/docs/documentation/server_admin/images/passkey-conditional-ui-autofill.png new file mode 100644 index 0000000000000000000000000000000000000000..5d488c32bc843d710ec32aa30c3f8aa913c8b51d GIT binary patch literal 38060 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 WUCr`%J>tC0M12m_)2LB{eE2`1FY*=u literal 0 HcmV?d00001 diff --git a/docs/documentation/server_admin/images/passkey-conditional-ui-flow.png b/docs/documentation/server_admin/images/passkey-conditional-ui-flow.png new file mode 100644 index 0000000000000000000000000000000000000000..f5d6bb90e2cb3df9d2623537bf824011b201e143 GIT binary patch literal 15670 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 literal 0 HcmV?d00001 diff --git a/docs/documentation/server_admin/topics/authentication/passkeys.adoc b/docs/documentation/server_admin/topics/authentication/passkeys.adoc index 6017af4f5a3..1b41103645f 100644 --- a/docs/documentation/server_admin/topics/authentication/passkeys.adoc +++ b/docs/documentation/server_admin/topics/authentication/passkeys.adoc @@ -12,3 +12,80 @@ Therefore, users of {project_name} can do Passkey registration and authenticatio Both synced Passkeys and device-bound Passkeys can be used for both Same-Device and Cross-Device Authentication (CDA). However, Passkeys operations success depends on the user's environment. Make sure which operations can succeed in https://passkeys.dev/device-support/[the environment]. ==== + +[[_passkeys-conditional-ui]] +==== Passkey Authentication with Conditional UI + +Passkey Authentication with Conditional UI can authenticate a user with its passkey in the same way as in xref:_webauthn_loginless[LoginLess WebAuthn]. +This authentication shows a user a list of passkeys stored on a device where the user runs a browser. +Therefore, the user can select one of the passkeys in the list to authenticate them. Compared with xref:_webauthn_loginless[LoginLess WebAuthn], the authentication improves the user's experience of authentication. + +[NOTE] +==== +This authentication uses the https://github.com/w3c/webauthn/wiki/Explainer:-WebAuthn-Conditional-UI/[WebAuthn Conditional UI]. +Therefore, this authentication success depends on the user's environment. +If the environment does not support WebAuthn Conditional UI, this authentication falls back to xref:_webauthn_loginless[LoginLess WebAuthn]. +==== + +:tech_feature_name: Passkey Authentication +:tech_feature_setting: -Dkeycloak.profile.feature.passkeys=enabled +:tech_feature_id: passkeys +include::../templates/techpreview.adoc[] + +.Procedure +===== Setup + +Set up Passkey Authentication with Conditional UI as follows: + +. (if not already present) Register a new required action for WebAuthn passwordless support. Use the steps described in <<_webauthn-register, Enable WebAuthn Authenticator Registration>>. Register the `Webauthn Register Passwordless` action. + +. Configure the `WebAuthn Passwordless Policy`. Perform the configuration in the Admin Console, `Authentication` section, in the tab `Policies` -> `WebAuthn Passwordless Policy`. Set *User Verification Requirement* to *required* and *Require discoverable credential* to *Yes* when you configure the policy for loginless scenario. Note that since there is no dedicated Loginless policy, it is impossible to mix authentication scenarios with user verification=no/discoverable credential=no and loginless scenarios (user verification=yes/discoverable credential=yes). ++ +NOTE: Storage capacity is usually very limited on hardware passkeys meaning that you cannot store many discoverable credentials on your passkey. However, this limitation may be mitigated for instance if you use an Android phone backed by a Google account as a passkey device or an iPhone backed by Bitwarden. + +. Configure the authentication flow. Create a new authentication flow, add the *Passkeys Conditional UI Authenticator* execution and set the Requirement setting of the execution to *Required*. ++ +The final configuration of the flow looks similar to this: +image:images/passkey-conditional-ui-flow.png[Passkey Authentication with Conditional UI flow flow] + +. Bind the flow above as a *browser* authentication flow in the realm as described in the <<_webauthn-register, WebAuthn section above>>. + +The authentication flow above requires that user must already have passkey credential on his or her account to be able to log in. This requirement means that all users in the realm must have passkeys already set. +That can be achieved for instance by enabling user registration as described below. + +===== Setup of the registration for passkeys conditional UI + +. Enable <> for your realm + +. In the <> of the realm, select flow *registration* and switch the authenticator *Password validation* to *Disabled*. +This means that newly registered users will not be required to create the passwords in this example setup. Users must always use passkeys instead of passwords. + +. Return to the *Required actions* sub-tab of the tab *Authentication* tab and find the `Webauthn Register Passwordless` action and mark it with *Set as default action*. +This means that it would be added to all new users after their registration. + +The alternative to the registration flow setup is to add the required action `WebAuthn Register Passwordless` to a user who is already known to {project_name}. The user with the required action configured will have to authenticate (with a username/password for example) and will then be prompted to register a passkey to be used for loginless authentication. + +[NOTE] +==== +We plan to improve the usability and allow integration of conditional passkeys with the existing authenticators and forms such as the default username / password form. +==== + +[NOTE] +==== +From https://www.w3.org/TR/webauthn-3/[Web Authn Level 3], *Resident Key* was replaced with *Discoverable Credential*. +==== + +If a user's browser supports https://github.com/w3c/webauthn/wiki/Explainer:-WebAuthn-Conditional-UI/[WebAuthn Conditional UI], the follwing screen is shown. + +.Passkey Authentication with Conditional UI +image:images/passkey-conditional-ui-authentication.png[Passkey Authentication with Conditional UI] + +When the user clicks the *Select your passkey* textbox, a list of passkeys stored on a device where the user runs a browse is shown as follows. + +.Passkey Authentication with Conditional UI Autofill +image:images/passkey-conditional-ui-autofill.png[Passkey Authentication with Conditional UI Autofill] + +If a user's browser does not support https://github.com/w3c/webauthn/wiki/Explainer:-WebAuthn-Conditional-UI/[WebAuthn Conditional UI], the authenticaion falls back to the xref:_webauthn_loginless[LoginLess WebAuthn] as follows. + +.Passkey Authentication with Conditional UI falling back to LoginLess WebAuthn +image:images/passkey-conditional-ui-fallback-authentication.png[Passkey Authentication with Conditional UI falling back to LoginLess WebAuthn] diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/browser/PasskeysConditionalUIAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/browser/PasskeysConditionalUIAuthenticator.java new file mode 100644 index 00000000000..7e50428adfc --- /dev/null +++ b/services/src/main/java/org/keycloak/authentication/authenticators/browser/PasskeysConditionalUIAuthenticator.java @@ -0,0 +1,43 @@ +/* + * Copyright 2023 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/** + * @author Takashi Norimatsu + */ +package org.keycloak.authentication.authenticators.browser; + +import org.keycloak.authentication.AuthenticationFlowContext; +import org.keycloak.models.KeycloakSession; + +import jakarta.ws.rs.core.Response; + +public class PasskeysConditionalUIAuthenticator extends WebAuthnPasswordlessAuthenticator { + + public PasskeysConditionalUIAuthenticator(KeycloakSession session) { + super(session); + } + + @Override + public void authenticate(AuthenticationFlowContext context) { + super.authenticate(context); + Response challenge = context.form() + .createForm("login-passkeys-conditional-authenticate.ftl"); + context.challenge(challenge); + } + +} diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/browser/PasskeysConditionalUIAuthenticatorFactory.java b/services/src/main/java/org/keycloak/authentication/authenticators/browser/PasskeysConditionalUIAuthenticatorFactory.java new file mode 100644 index 00000000000..15f54a97ec9 --- /dev/null +++ b/services/src/main/java/org/keycloak/authentication/authenticators/browser/PasskeysConditionalUIAuthenticatorFactory.java @@ -0,0 +1,63 @@ +/* + * Copyright 2023 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.keycloak.authentication.authenticators.browser; + +import org.keycloak.Config; +import org.keycloak.authentication.Authenticator; +import org.keycloak.common.Profile; +import org.keycloak.models.KeycloakSession; +import org.keycloak.provider.EnvironmentDependentProviderFactory; + +/** + * @author Takashi Norimatsu + */ +public class PasskeysConditionalUIAuthenticatorFactory extends WebAuthnPasswordlessAuthenticatorFactory implements EnvironmentDependentProviderFactory { + + public static final String PROVIDER_ID = "passkeys-authenticator"; + + @Override + public String getDisplayType() { + return "Passkeys Conditional UI Authenticator"; + } + + @Override + public String getHelpText() { + return "Authenticator for Passkeys with conditional UI. A list of passkeys stored on a device where a browser is running is automatically shown. Due to characteristics of conditional UI, it is used for login-less authentication."; + } + + @Override + public Authenticator create(KeycloakSession session) { + return new PasskeysConditionalUIAuthenticator(session); + } + + @Override + public void init(Config.Scope config) { + } + + @Override + public boolean isSupported(Config.Scope config) { + return Profile.isFeatureEnabled(Profile.Feature.PASSKEYS); + } + + @Override + public String getId() { + return PROVIDER_ID; + } + +} diff --git a/services/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory b/services/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory index 5af2ca8b4dd..8a15cee7dc6 100755 --- a/services/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory +++ b/services/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory @@ -53,4 +53,5 @@ org.keycloak.authentication.authenticators.access.DenyAccessAuthenticatorFactory org.keycloak.authentication.authenticators.access.AllowAccessAuthenticatorFactory org.keycloak.authentication.authenticators.sessionlimits.UserSessionLimitsAuthenticatorFactory org.keycloak.authentication.authenticators.browser.RecoveryAuthnCodesFormAuthenticatorFactory -org.keycloak.organization.authentication.authenticators.browser.OrganizationAuthenticatorFactory \ No newline at end of file +org.keycloak.organization.authentication.authenticators.browser.OrganizationAuthenticatorFactory +org.keycloak.authentication.authenticators.browser.PasskeysConditionalUIAuthenticatorFactory \ No newline at end of file diff --git a/testsuite/integration-arquillian/tests/other/webauthn/src/main/java/org/keycloak/testsuite/webauthn/pages/WebAuthnAuthenticatorsList.java b/testsuite/integration-arquillian/tests/other/webauthn/src/main/java/org/keycloak/testsuite/webauthn/pages/WebAuthnAuthenticatorsList.java index fec10bac1b9..ec4a1ef1838 100644 --- a/testsuite/integration-arquillian/tests/other/webauthn/src/main/java/org/keycloak/testsuite/webauthn/pages/WebAuthnAuthenticatorsList.java +++ b/testsuite/integration-arquillian/tests/other/webauthn/src/main/java/org/keycloak/testsuite/webauthn/pages/WebAuthnAuthenticatorsList.java @@ -37,17 +37,22 @@ import static org.keycloak.testsuite.util.UIUtils.getTextFromElementOrNull; */ public class WebAuthnAuthenticatorsList { - @FindBy(className = "kc-webauthn-authenticator") + @FindBy(xpath = "//div[contains(@id,'kc-webauthn-authenticator-item-')]") private List authenticators; public List getItems() { try { List items = new ArrayList<>(); - for (WebElement auth : authenticators) { - String name = getTextFromElementOrNull(() -> auth.findElement(By.className("kc-webauthn-authenticator-label"))); - String createdAt = getTextFromElementOrNull(() -> auth.findElement(By.className("kc-webauthn-authenticator-created"))); - String createdAtLabel = getTextFromElementOrNull(() -> auth.findElement(By.className("kc-webauthn-authenticator-created-label"))); - String transport = getTextFromElementOrNull(() -> auth.findElement(By.className("kc-webauthn-authenticator-transport"))); + for (int i = 0; i < authenticators.size(); i++) { + WebElement auth = authenticators.get(i); + final String nameId = "kc-webauthn-authenticator-label-" + i; + String name = getTextFromElementOrNull(() -> auth.findElement(By.id(nameId))); + final String createdAtId = "kc-webauthn-authenticator-created-" + i; + String createdAt = getTextFromElementOrNull(() -> auth.findElement(By.id(createdAtId))); + final String createdAtLabelId = "kc-webauthn-authenticator-createdlabel-" + i; + String createdAtLabel = getTextFromElementOrNull(() -> auth.findElement(By.id(createdAtLabelId))); + final String transportId = "kc-webauthn-authenticator-transport-" + i; + String transport = getTextFromElementOrNull(() -> auth.findElement(By.id(transportId))); items.add(new WebAuthnAuthenticatorItem(name, createdAt, createdAtLabel, transport)); } return items; diff --git a/testsuite/integration-arquillian/tests/other/webauthn/src/main/java/org/keycloak/testsuite/webauthn/pages/WebAuthnLoginPage.java b/testsuite/integration-arquillian/tests/other/webauthn/src/main/java/org/keycloak/testsuite/webauthn/pages/WebAuthnLoginPage.java index e3ac912a46d..ccee82501fa 100644 --- a/testsuite/integration-arquillian/tests/other/webauthn/src/main/java/org/keycloak/testsuite/webauthn/pages/WebAuthnLoginPage.java +++ b/testsuite/integration-arquillian/tests/other/webauthn/src/main/java/org/keycloak/testsuite/webauthn/pages/WebAuthnLoginPage.java @@ -35,7 +35,7 @@ public class WebAuthnLoginPage extends LanguageComboboxAwarePage { @FindBy(id = "authenticateWebAuthnButton") private WebElement authenticateButton; - @FindBy(className = "kc-webauthn-authenticator-label") + @FindBy(xpath = "//div[contains(@id,'kc-webauthn-authenticator-label-')]") private List authenticatorsLabels; @Page diff --git a/themes/src/main/resources/theme/base/login/login-passkeys-conditional-authenticate.ftl b/themes/src/main/resources/theme/base/login/login-passkeys-conditional-authenticate.ftl new file mode 100644 index 00000000000..81be11365c6 --- /dev/null +++ b/themes/src/main/resources/theme/base/login/login-passkeys-conditional-authenticate.ftl @@ -0,0 +1,143 @@ +<#import "template.ftl" as layout> +<@layout.registrationLayout displayInfo=(realm.registrationAllowed && !registrationDisabled??); section> + <#if section = "title"> + title + <#elseif section = "header"> + ${kcSanitize(msg("passkey-login-title"))?no_esc} + <#elseif section = "form"> +
+ + + + + + +
+ +
+ <#if authenticators??> +
+ <#list authenticators.authenticators as authenticator> + + +
+ + <#if shouldDisplayAuthenticators?? && shouldDisplayAuthenticators> + <#if authenticators.authenticators?size gt 1> +

${kcSanitize(msg("passkey-available-authenticators"))?no_esc}

+ + +
+ <#list authenticators.authenticators as authenticator> +
+
+ +
+
+
+ ${kcSanitize(msg('${authenticator.label}'))?no_esc} +
+ + <#if authenticator.transports?? && authenticator.transports.displayNameProperties?has_content> +
+ <#list authenticator.transports.displayNameProperties as nameProperty> + ${kcSanitize(msg('${nameProperty!}'))?no_esc} + <#if nameProperty?has_next> + , + + +
+ + +
+ + ${kcSanitize(msg('passkey-createdAt-label'))?no_esc} + + + ${kcSanitize(authenticator.createdAt)?no_esc} + +
+
+
+
+ +
+ + + +
+
+ <#if realm.password> + + + + +
+
+
+ + + + <#elseif section = "info"> + <#if realm.registrationAllowed && !registrationDisabled??> +
+ ${msg("noAccount")} ${msg("doRegister")} +
+ + + + diff --git a/themes/src/main/resources/theme/base/login/messages/messages_en.properties b/themes/src/main/resources/theme/base/login/messages/messages_en.properties index 5f5e5ca3006..d8172f1f31b 100755 --- a/themes/src/main/resources/theme/base/login/messages/messages_en.properties +++ b/themes/src/main/resources/theme/base/login/messages/messages_en.properties @@ -480,6 +480,14 @@ webauthn-error-auth-verification=Passkey authentication result is invalid.
webauthn-error-register-verification=Passkey registration result is invalid.
{0} webauthn-error-user-not-found=Unknown user authenticated by the Passkey. +# Passkey +passkey-login-title=Passkey login +passkey-available-authenticators=Available Passkeys +passkey-unsupported-browser-text=Passkey is not supported by this browser. Try another one or contact your administrator. +passkey-doAuthenticate=Sign in with Passkey +passkey-createdAt-label=Created +passkey-autofill-select=Select your passkey + # Identity provider identity-provider-redirector=Connect with another Identity Provider identity-provider-login-label=Or sign in with diff --git a/themes/src/main/resources/theme/base/login/resources/js/passkeysConditionalAuth.js b/themes/src/main/resources/theme/base/login/resources/js/passkeysConditionalAuth.js new file mode 100644 index 00000000000..f94e26ee98c --- /dev/null +++ b/themes/src/main/resources/theme/base/login/resources/js/passkeysConditionalAuth.js @@ -0,0 +1,79 @@ +import { base64url } from "rfc4648"; +import { returnSuccess, returnFailure } from "./webauthnAuthenticate.js"; + +export function initAuthenticate(input) { + // Check if WebAuthn is supported by this browser + if (!window.PublicKeyCredential) { + returnFailure(input.errmsg); + return; + } + if (input.isUserIdentified || typeof PublicKeyCredential.isConditionalMediationAvailable === "undefined") { + document.getElementById("kc-form-passkey-button").style.display = 'block'; + } else { + tryAutoFillUI(input); + } +} + +function doAuthenticate(input) { + // Check if WebAuthn is supported by this browser + if (!window.PublicKeyCredential) { + returnFailure(input.errmsg); + return; + } + + const publicKey = { + rpId : input.rpId, + challenge: base64url.parse(input.challenge, { loose: true }) + }; + + publicKey.allowCredentials = !input.isUserIdentified ? [] : getAllowCredentials(); + + if (input.createTimeout !== 0) { + publicKey.timeout = input.createTimeout * 1000; + } + + if (input.userVerification !== 'not specified') { + publicKey.userVerification = input.userVerification; + } + + return navigator.credentials.get({ + publicKey: publicKey, + ...input.additionalOptions + }); +} + +async function tryAutoFillUI(input) { + const isConditionalMediationAvailable = await PublicKeyCredential.isConditionalMediationAvailable(); + if (isConditionalMediationAvailable) { + document.getElementById("kc-form-login").style.display = "block"; + input.additionalOptions = { mediation: 'conditional'}; + try { + const result = await doAuthenticate(input); + returnSuccess(result); + } catch (error) { + returnFailure(error); + } + } else { + document.getElementById("kc-form-passkey-button").style.display = 'block'; + } +} + +function getAllowCredentials() { + const allowCredentials = []; + const authnUse = document.forms['authn_select'].authn_use_chk; + if (authnUse !== undefined) { + if (authnUse.length === undefined) { + allowCredentials.push({ + id: base64url.parse(authnUse.value, {loose: true}), + type: 'public-key', + }); + } else { + authnUse.forEach((entry) => + allowCredentials.push({ + id: base64url.parse(entry.value, {loose: true}), + type: 'public-key', + })); + } + } + return allowCredentials; +} diff --git a/themes/src/main/resources/theme/base/login/resources/js/webauthnAuthenticate.js b/themes/src/main/resources/theme/base/login/resources/js/webauthnAuthenticate.js index a9639ca967e..eaa05b69f52 100644 --- a/themes/src/main/resources/theme/base/login/resources/js/webauthnAuthenticate.js +++ b/themes/src/main/resources/theme/base/login/resources/js/webauthnAuthenticate.js @@ -65,7 +65,7 @@ function doAuthenticate(allowCredentials, challenge, userVerification, rpId, cre return navigator.credentials.get({publicKey}); } -function returnSuccess(result) { +export function returnSuccess(result) { document.getElementById("clientDataJSON").value = base64url.stringify(new Uint8Array(result.response.clientDataJSON), { pad: false }); document.getElementById("authenticatorData").value = base64url.stringify(new Uint8Array(result.response.authenticatorData), { pad: false }); document.getElementById("signature").value = base64url.stringify(new Uint8Array(result.response.signature), { pad: false }); @@ -76,7 +76,7 @@ function returnSuccess(result) { document.getElementById("webauth").submit(); } -function returnFailure(err) { +export function returnFailure(err) { document.getElementById("error").value = err; document.getElementById("webauth").submit(); } \ No newline at end of file diff --git a/themes/src/main/resources/theme/base/login/webauthn-authenticate.ftl b/themes/src/main/resources/theme/base/login/webauthn-authenticate.ftl index 10b66bacd8e..8148e8003c0 100644 --- a/themes/src/main/resources/theme/base/login/webauthn-authenticate.ftl +++ b/themes/src/main/resources/theme/base/login/webauthn-authenticate.ftl @@ -1,5 +1,5 @@ <#import "template.ftl" as layout> -<@layout.registrationLayout displayMessage=!messagesPerField.existsError('username') displayInfo=(realm.password && realm.registrationAllowed && !registrationDisabled??); section> +<@layout.registrationLayout displayInfo=(realm.registrationAllowed && !registrationDisabled??); section> <#if section = "title"> title <#elseif section = "header"> @@ -30,19 +30,19 @@
<#list authenticators.authenticators as authenticator> -
+
-
+
${kcSanitize(msg('${authenticator.label}'))?no_esc}
<#if authenticator.transports?? && authenticator.transports.displayNameProperties?has_content> -
+
<#list authenticator.transports.displayNameProperties as nameProperty> ${kcSanitize(msg('${nameProperty!}'))?no_esc} <#if nameProperty?has_next> @@ -53,10 +53,10 @@
- + ${kcSanitize(msg('webauthn-createdAt-label'))?no_esc} - + ${kcSanitize(authenticator.createdAt)?no_esc}
@@ -93,7 +93,7 @@ <#elseif section = "info"> - <#if realm.password && realm.registrationAllowed && !registrationDisabled??> + <#if realm.registrationAllowed && !registrationDisabled??>
${msg("noAccount")} ${msg("doRegister")}