Changing default passwordless webauthn policy to follow recommended values in the documentation

Closes #40792

Signed-off-by: rmartinc <rmartinc@redhat.com>
This commit is contained in:
rmartinc 2025-06-30 15:59:09 +02:00 committed by Marek Posolda
parent 6b050776bc
commit 900d8c7400
21 changed files with 341 additions and 118 deletions

View File

@ -932,7 +932,7 @@ public class RealmAdapter implements StorageProviderRealmModel, JpaModel<RealmEn
@Override
public WebAuthnPolicy getWebAuthnPolicy() {
return getWebAuthnPolicy("");
return getWebAuthnPolicy("", WebAuthnPolicyTwoFactorDefaults.get());
}
@Override
@ -943,7 +943,7 @@ public class RealmAdapter implements StorageProviderRealmModel, JpaModel<RealmEn
@Override
public WebAuthnPolicy getWebAuthnPolicyPasswordless() {
// We will use some prefix for attributes related to passwordless WebAuthn policy
return getWebAuthnPolicy(Constants.WEBAUTHN_PASSWORDLESS_PREFIX);
return getWebAuthnPolicy(Constants.WEBAUTHN_PASSWORDLESS_PREFIX, WebAuthnPolicyPasswordlessDefaults.get());
}
@Override
@ -952,68 +952,81 @@ public class RealmAdapter implements StorageProviderRealmModel, JpaModel<RealmEn
setWebAuthnPolicy(policy, Constants.WEBAUTHN_PASSWORDLESS_PREFIX);
}
private WebAuthnPolicy getWebAuthnPolicy(String attributePrefix) {
private WebAuthnPolicy getWebAuthnPolicy(String attributePrefix, WebAuthnPolicy defaultConfig) {
WebAuthnPolicy policy = new WebAuthnPolicy();
// mandatory parameters
String rpEntityName = getAttribute(RealmAttributes.WEBAUTHN_POLICY_RP_ENTITY_NAME + attributePrefix);
if (rpEntityName == null || rpEntityName.isEmpty())
rpEntityName = Constants.DEFAULT_WEBAUTHN_POLICY_RP_ENTITY_NAME;
rpEntityName = defaultConfig.getRpEntityName();
policy.setRpEntityName(rpEntityName);
String signatureAlgorithmsString = getAttribute(RealmAttributes.WEBAUTHN_POLICY_SIGNATURE_ALGORITHMS + attributePrefix);
if (signatureAlgorithmsString == null || signatureAlgorithmsString.isEmpty())
signatureAlgorithmsString = Constants.DEFAULT_WEBAUTHN_POLICY_SIGNATURE_ALGORITHMS;
List<String> signatureAlgorithms = Arrays.asList(signatureAlgorithmsString.split(","));
List<String> signatureAlgorithms = (signatureAlgorithmsString == null || signatureAlgorithmsString.isEmpty())
? defaultConfig.getSignatureAlgorithm()
: Arrays.asList(signatureAlgorithmsString.split(","));
policy.setSignatureAlgorithm(signatureAlgorithms);
// optional parameters
String rpId = getAttribute(RealmAttributes.WEBAUTHN_POLICY_RP_ID + attributePrefix);
if (rpId == null || rpId.isEmpty()) rpId = "";
if (rpId == null || rpId.isEmpty()) {
rpId = defaultConfig.getRpId();
}
policy.setRpId(rpId);
String attestationConveyancePreference = getAttribute(RealmAttributes.WEBAUTHN_POLICY_ATTESTATION_CONVEYANCE_PREFERENCE + attributePrefix);
if (attestationConveyancePreference == null || attestationConveyancePreference.isEmpty())
attestationConveyancePreference = Constants.DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED;
if (attestationConveyancePreference == null || attestationConveyancePreference.isEmpty()) {
attestationConveyancePreference = defaultConfig.getAttestationConveyancePreference();
}
policy.setAttestationConveyancePreference(attestationConveyancePreference);
String authenticatorAttachment = getAttribute(RealmAttributes.WEBAUTHN_POLICY_AUTHENTICATOR_ATTACHMENT + attributePrefix);
if (authenticatorAttachment == null || authenticatorAttachment.isEmpty())
authenticatorAttachment = Constants.DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED;
if (authenticatorAttachment == null || authenticatorAttachment.isEmpty()) {
authenticatorAttachment = defaultConfig.getAuthenticatorAttachment();
}
policy.setAuthenticatorAttachment(authenticatorAttachment);
String requireResidentKey = getAttribute(RealmAttributes.WEBAUTHN_POLICY_REQUIRE_RESIDENT_KEY + attributePrefix);
if (requireResidentKey == null || requireResidentKey.isEmpty())
requireResidentKey = Constants.DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED;
if (requireResidentKey == null || requireResidentKey.isEmpty()) {
requireResidentKey = defaultConfig.getRequireResidentKey();
}
policy.setRequireResidentKey(requireResidentKey);
String userVerificationRequirement = getAttribute(RealmAttributes.WEBAUTHN_POLICY_USER_VERIFICATION_REQUIREMENT + attributePrefix);
if (userVerificationRequirement == null || userVerificationRequirement.isEmpty())
userVerificationRequirement = Constants.DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED;
if (userVerificationRequirement == null || userVerificationRequirement.isEmpty()) {
userVerificationRequirement = defaultConfig.getUserVerificationRequirement();
}
policy.setUserVerificationRequirement(userVerificationRequirement);
String createTime = getAttribute(RealmAttributes.WEBAUTHN_POLICY_CREATE_TIMEOUT + attributePrefix);
if (createTime != null) policy.setCreateTimeout(Integer.parseInt(createTime));
else policy.setCreateTimeout(0);
String createTimeoutString = getAttribute(RealmAttributes.WEBAUTHN_POLICY_CREATE_TIMEOUT + attributePrefix);
int createTimeout = (createTimeoutString != null)
? Integer.parseInt(createTimeoutString)
: defaultConfig.getCreateTimeout();
policy.setCreateTimeout(createTimeout);
String avoidSameAuthenticatorRegister = getAttribute(RealmAttributes.WEBAUTHN_POLICY_AVOID_SAME_AUTHENTICATOR_REGISTER + attributePrefix);
if (avoidSameAuthenticatorRegister != null) policy.setAvoidSameAuthenticatorRegister(Boolean.parseBoolean(avoidSameAuthenticatorRegister));
String avoidSameAuthenticatorRegisterString = getAttribute(RealmAttributes.WEBAUTHN_POLICY_AVOID_SAME_AUTHENTICATOR_REGISTER + attributePrefix);
boolean avoidSameAuthenticatorRegister = (avoidSameAuthenticatorRegisterString != null)
? Boolean.parseBoolean(avoidSameAuthenticatorRegisterString)
: defaultConfig.isAvoidSameAuthenticatorRegister();
policy.setAvoidSameAuthenticatorRegister(avoidSameAuthenticatorRegister);
String acceptableAaguidsString = getAttribute(RealmAttributes.WEBAUTHN_POLICY_ACCEPTABLE_AAGUIDS + attributePrefix);
List<String> acceptableAaguids = new ArrayList<>();
if (acceptableAaguidsString != null && !acceptableAaguidsString.isEmpty())
acceptableAaguids = Arrays.asList(acceptableAaguidsString.split(","));
List<String> acceptableAaguids = (acceptableAaguidsString != null && !acceptableAaguidsString.isEmpty())
? Arrays.asList(acceptableAaguidsString.split(","))
: defaultConfig.getAcceptableAaguids();
policy.setAcceptableAaguids(acceptableAaguids);
String extraOriginsString = getAttribute(RealmAttributes.WEBAUTHN_POLICY_EXTRA_ORIGINS + attributePrefix);
List<String> extraOrigins = new ArrayList<>();
if (extraOriginsString != null && !extraOriginsString.isEmpty())
extraOrigins = Arrays.asList(extraOriginsString.split(","));
List<String> extraOrigins = (extraOriginsString != null && !extraOriginsString.isEmpty())
? Arrays.asList(extraOriginsString.split(","))
: defaultConfig.getExtraOrigins();
policy.setExtraOrigins(extraOrigins);
String passkeysEnabled = getAttribute(RealmAttributes.WEBAUTHN_POLICY_PASSKEYS_ENABLED + attributePrefix);
if (passkeysEnabled != null) policy.setPasskeysEnabled(Boolean.parseBoolean(passkeysEnabled));
String passkeysEnabledString = getAttribute(RealmAttributes.WEBAUTHN_POLICY_PASSKEYS_ENABLED + attributePrefix);
Boolean passKeysEnabled = (passkeysEnabledString != null)
? Boolean.valueOf(passkeysEnabledString)
: defaultConfig.isPasskeysEnabled();
policy.setPasskeysEnabled(passKeysEnabled);
return policy;
}

View File

@ -59,12 +59,13 @@ import org.keycloak.models.ScopeContainerModel;
import org.keycloak.models.UserConsentModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.WebAuthnPolicy;
import org.keycloak.models.WebAuthnPolicyPasswordlessDefaults;
import org.keycloak.models.WebAuthnPolicyTwoFactorDefaults;
import org.keycloak.models.utils.ComponentUtil;
import org.keycloak.models.utils.DefaultAuthenticationFlows;
import org.keycloak.models.utils.DefaultKeyProviders;
import org.keycloak.models.utils.DefaultRequiredActions;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.models.utils.RepresentationToModel;
import org.keycloak.organization.OrganizationProvider;
import org.keycloak.organization.validation.OrganizationsValidation;
@ -1262,54 +1263,66 @@ public class DefaultExportImportManager implements ExportImportManager {
private static WebAuthnPolicy getWebAuthnPolicyTwoFactor(RealmRepresentation rep) {
WebAuthnPolicy webAuthnPolicy = new WebAuthnPolicy();
WebAuthnPolicy defaultConfig = WebAuthnPolicyTwoFactorDefaults.get();
String webAuthnPolicyRpEntityName = rep.getWebAuthnPolicyRpEntityName();
if (webAuthnPolicyRpEntityName == null || webAuthnPolicyRpEntityName.isEmpty())
webAuthnPolicyRpEntityName = Constants.DEFAULT_WEBAUTHN_POLICY_RP_ENTITY_NAME;
webAuthnPolicyRpEntityName = defaultConfig.getRpEntityName();
webAuthnPolicy.setRpEntityName(webAuthnPolicyRpEntityName);
List<String> webAuthnPolicySignatureAlgorithms = rep.getWebAuthnPolicySignatureAlgorithms();
if (webAuthnPolicySignatureAlgorithms == null || webAuthnPolicySignatureAlgorithms.isEmpty())
webAuthnPolicySignatureAlgorithms = Arrays.asList(Constants.DEFAULT_WEBAUTHN_POLICY_SIGNATURE_ALGORITHMS.split(","));
webAuthnPolicySignatureAlgorithms = defaultConfig.getSignatureAlgorithm();
webAuthnPolicy.setSignatureAlgorithm(webAuthnPolicySignatureAlgorithms);
String webAuthnPolicyRpId = rep.getWebAuthnPolicyRpId();
if (webAuthnPolicyRpId == null || webAuthnPolicyRpId.isEmpty())
webAuthnPolicyRpId = "";
webAuthnPolicyRpId = defaultConfig.getRpId();
webAuthnPolicy.setRpId(webAuthnPolicyRpId);
String webAuthnPolicyAttestationConveyancePreference = rep.getWebAuthnPolicyAttestationConveyancePreference();
if (webAuthnPolicyAttestationConveyancePreference == null || webAuthnPolicyAttestationConveyancePreference.isEmpty())
webAuthnPolicyAttestationConveyancePreference = Constants.DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED;
webAuthnPolicyAttestationConveyancePreference = defaultConfig.getAttestationConveyancePreference();
webAuthnPolicy.setAttestationConveyancePreference(webAuthnPolicyAttestationConveyancePreference);
String webAuthnPolicyAuthenticatorAttachment = rep.getWebAuthnPolicyAuthenticatorAttachment();
if (webAuthnPolicyAuthenticatorAttachment == null || webAuthnPolicyAuthenticatorAttachment.isEmpty())
webAuthnPolicyAuthenticatorAttachment = Constants.DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED;
webAuthnPolicyAuthenticatorAttachment = defaultConfig.getAuthenticatorAttachment();
webAuthnPolicy.setAuthenticatorAttachment(webAuthnPolicyAuthenticatorAttachment);
String webAuthnPolicyRequireResidentKey = rep.getWebAuthnPolicyRequireResidentKey();
if (webAuthnPolicyRequireResidentKey == null || webAuthnPolicyRequireResidentKey.isEmpty())
webAuthnPolicyRequireResidentKey = Constants.DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED;
webAuthnPolicyRequireResidentKey = defaultConfig.getRequireResidentKey();
webAuthnPolicy.setRequireResidentKey(webAuthnPolicyRequireResidentKey);
String webAuthnPolicyUserVerificationRequirement = rep.getWebAuthnPolicyUserVerificationRequirement();
if (webAuthnPolicyUserVerificationRequirement == null || webAuthnPolicyUserVerificationRequirement.isEmpty())
webAuthnPolicyUserVerificationRequirement = Constants.DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED;
webAuthnPolicyUserVerificationRequirement = defaultConfig.getUserVerificationRequirement();
webAuthnPolicy.setUserVerificationRequirement(webAuthnPolicyUserVerificationRequirement);
Integer webAuthnPolicyCreateTimeout = rep.getWebAuthnPolicyCreateTimeout();
if (webAuthnPolicyCreateTimeout != null) webAuthnPolicy.setCreateTimeout(webAuthnPolicyCreateTimeout);
else webAuthnPolicy.setCreateTimeout(0);
if (webAuthnPolicyCreateTimeout == null) {
webAuthnPolicyCreateTimeout = defaultConfig.getCreateTimeout();
}
webAuthnPolicy.setCreateTimeout(webAuthnPolicyCreateTimeout);
Boolean webAuthnPolicyAvoidSameAuthenticatorRegister = rep.isWebAuthnPolicyAvoidSameAuthenticatorRegister();
if (webAuthnPolicyAvoidSameAuthenticatorRegister != null) webAuthnPolicy.setAvoidSameAuthenticatorRegister(webAuthnPolicyAvoidSameAuthenticatorRegister);
if (webAuthnPolicyAvoidSameAuthenticatorRegister == null) {
webAuthnPolicyAvoidSameAuthenticatorRegister = defaultConfig.isAvoidSameAuthenticatorRegister();
}
webAuthnPolicy.setAvoidSameAuthenticatorRegister(webAuthnPolicyAvoidSameAuthenticatorRegister);
List<String> webAuthnPolicyAcceptableAaguids = rep.getWebAuthnPolicyAcceptableAaguids();
if (webAuthnPolicyAcceptableAaguids != null) webAuthnPolicy.setAcceptableAaguids(webAuthnPolicyAcceptableAaguids);
if (webAuthnPolicyAcceptableAaguids == null) {
webAuthnPolicyAcceptableAaguids = defaultConfig.getAcceptableAaguids();
}
webAuthnPolicy.setAcceptableAaguids(webAuthnPolicyAcceptableAaguids);
List<String> webAuthnPolicyExtraOrigins = rep.getWebAuthnPolicyExtraOrigins();
if (webAuthnPolicyExtraOrigins != null) webAuthnPolicy.setExtraOrigins(webAuthnPolicyExtraOrigins);
if (webAuthnPolicyExtraOrigins == null) {
webAuthnPolicyExtraOrigins = defaultConfig.getExtraOrigins();
}
webAuthnPolicy.setExtraOrigins(webAuthnPolicyExtraOrigins);
return webAuthnPolicy;
}
@ -1317,57 +1330,72 @@ public class DefaultExportImportManager implements ExportImportManager {
private static WebAuthnPolicy getWebAuthnPolicyPasswordless(RealmRepresentation rep) {
WebAuthnPolicy webAuthnPolicy = new WebAuthnPolicy();
WebAuthnPolicy defaultConfig = WebAuthnPolicyPasswordlessDefaults.get();
String webAuthnPolicyRpEntityName = rep.getWebAuthnPolicyPasswordlessRpEntityName();
if (webAuthnPolicyRpEntityName == null || webAuthnPolicyRpEntityName.isEmpty())
webAuthnPolicyRpEntityName = Constants.DEFAULT_WEBAUTHN_POLICY_RP_ENTITY_NAME;
webAuthnPolicyRpEntityName = defaultConfig.getRpEntityName();
webAuthnPolicy.setRpEntityName(webAuthnPolicyRpEntityName);
List<String> webAuthnPolicySignatureAlgorithms = rep.getWebAuthnPolicyPasswordlessSignatureAlgorithms();
if (webAuthnPolicySignatureAlgorithms == null || webAuthnPolicySignatureAlgorithms.isEmpty())
webAuthnPolicySignatureAlgorithms = Arrays.asList(Constants.DEFAULT_WEBAUTHN_POLICY_SIGNATURE_ALGORITHMS.split(","));
webAuthnPolicySignatureAlgorithms = defaultConfig.getSignatureAlgorithm();
webAuthnPolicy.setSignatureAlgorithm(webAuthnPolicySignatureAlgorithms);
String webAuthnPolicyRpId = rep.getWebAuthnPolicyPasswordlessRpId();
if (webAuthnPolicyRpId == null || webAuthnPolicyRpId.isEmpty())
webAuthnPolicyRpId = "";
webAuthnPolicyRpId = defaultConfig.getRpId();
webAuthnPolicy.setRpId(webAuthnPolicyRpId);
String webAuthnPolicyAttestationConveyancePreference = rep.getWebAuthnPolicyPasswordlessAttestationConveyancePreference();
if (webAuthnPolicyAttestationConveyancePreference == null || webAuthnPolicyAttestationConveyancePreference.isEmpty())
webAuthnPolicyAttestationConveyancePreference = Constants.DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED;
webAuthnPolicyAttestationConveyancePreference = defaultConfig.getAttestationConveyancePreference();
webAuthnPolicy.setAttestationConveyancePreference(webAuthnPolicyAttestationConveyancePreference);
String webAuthnPolicyAuthenticatorAttachment = rep.getWebAuthnPolicyPasswordlessAuthenticatorAttachment();
if (webAuthnPolicyAuthenticatorAttachment == null || webAuthnPolicyAuthenticatorAttachment.isEmpty())
webAuthnPolicyAuthenticatorAttachment = Constants.DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED;
webAuthnPolicyAuthenticatorAttachment = defaultConfig.getAuthenticatorAttachment();
webAuthnPolicy.setAuthenticatorAttachment(webAuthnPolicyAuthenticatorAttachment);
String webAuthnPolicyRequireResidentKey = rep.getWebAuthnPolicyPasswordlessRequireResidentKey();
if (webAuthnPolicyRequireResidentKey == null || webAuthnPolicyRequireResidentKey.isEmpty())
webAuthnPolicyRequireResidentKey = Constants.DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED;
webAuthnPolicyRequireResidentKey = defaultConfig.getRequireResidentKey();
webAuthnPolicy.setRequireResidentKey(webAuthnPolicyRequireResidentKey);
String webAuthnPolicyUserVerificationRequirement = rep.getWebAuthnPolicyPasswordlessUserVerificationRequirement();
if (webAuthnPolicyUserVerificationRequirement == null || webAuthnPolicyUserVerificationRequirement.isEmpty())
webAuthnPolicyUserVerificationRequirement = Constants.DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED;
webAuthnPolicyUserVerificationRequirement = defaultConfig.getUserVerificationRequirement();
webAuthnPolicy.setUserVerificationRequirement(webAuthnPolicyUserVerificationRequirement);
Integer webAuthnPolicyCreateTimeout = rep.getWebAuthnPolicyPasswordlessCreateTimeout();
if (webAuthnPolicyCreateTimeout != null) webAuthnPolicy.setCreateTimeout(webAuthnPolicyCreateTimeout);
else webAuthnPolicy.setCreateTimeout(0);
if (webAuthnPolicyCreateTimeout == null) {
webAuthnPolicyCreateTimeout = defaultConfig.getCreateTimeout();
}
webAuthnPolicy.setCreateTimeout(webAuthnPolicyCreateTimeout);
Boolean webAuthnPolicyAvoidSameAuthenticatorRegister = rep.isWebAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister();
if (webAuthnPolicyAvoidSameAuthenticatorRegister != null) webAuthnPolicy.setAvoidSameAuthenticatorRegister(webAuthnPolicyAvoidSameAuthenticatorRegister);
if (webAuthnPolicyAvoidSameAuthenticatorRegister == null) {
webAuthnPolicyAvoidSameAuthenticatorRegister = defaultConfig.isAvoidSameAuthenticatorRegister();
}
webAuthnPolicy.setAvoidSameAuthenticatorRegister(webAuthnPolicyAvoidSameAuthenticatorRegister);
List<String> webAuthnPolicyAcceptableAaguids = rep.getWebAuthnPolicyPasswordlessAcceptableAaguids();
if (webAuthnPolicyAcceptableAaguids != null) webAuthnPolicy.setAcceptableAaguids(webAuthnPolicyAcceptableAaguids);
if (webAuthnPolicyAcceptableAaguids == null) {
webAuthnPolicyAcceptableAaguids = defaultConfig.getAcceptableAaguids();
}
webAuthnPolicy.setAcceptableAaguids(webAuthnPolicyAcceptableAaguids);
List<String> webAuthnPolicyExtraOrigins = rep.getWebAuthnPolicyPasswordlessExtraOrigins();
if (webAuthnPolicyExtraOrigins != null) webAuthnPolicy.setExtraOrigins(webAuthnPolicyExtraOrigins);
if (webAuthnPolicyExtraOrigins == null) {
webAuthnPolicyExtraOrigins = defaultConfig.getExtraOrigins();
}
webAuthnPolicy.setExtraOrigins(webAuthnPolicyExtraOrigins);
Boolean webAuthnPolicyPasswordlessPasskeysEnabled = rep.getWebAuthnPolicyPasswordlessPasskeysEnabled();
if (webAuthnPolicyPasswordlessPasskeysEnabled != null) webAuthnPolicy.setPasskeysEnabled(webAuthnPolicyPasswordlessPasskeysEnabled);
if (webAuthnPolicyPasswordlessPasskeysEnabled == null) {
webAuthnPolicyPasswordlessPasskeysEnabled = defaultConfig.isPasskeysEnabled();
}
webAuthnPolicy.setPasskeysEnabled(webAuthnPolicyPasswordlessPasskeysEnabled);
return webAuthnPolicy;
}

View File

@ -70,6 +70,11 @@ public final class Constants {
public static final String DEFAULT_WEBAUTHN_POLICY_RP_ENTITY_NAME = "keycloak";
// it stands for optional parameter not specified in WebAuthn
public static final String DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED = "not specified";
public static final String WEBAUTHN_POLICY_OPTION_REQUIRED = "required";
public static final String WEBAUTHN_POLICY_OPTION_PREFERED = "preferred";
public static final String WEBAUTHN_POLICY_OPTION_DISCOURAGED = "discouraged";
public static final String WEBAUTHN_POLICY_OPTION_YES = "Yes";
public static final String WEBAUTHN_POLICY_OPTION_NO = "No";
// Prefix used for the realm attributes and other places
public static final String WEBAUTHN_PASSWORDLESS_PREFIX = "Passwordless";

View File

@ -0,0 +1,36 @@
/*
* Copyright 2025 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.models;
/**
* Default values for the WebAuthn configuration when used as passwordless.
*
* @author rmartinc
*/
public class WebAuthnPolicyPasswordlessDefaults extends WebAuthnPolicyTwoFactorDefaults {
public static WebAuthnPolicy get() {
return new WebAuthnPolicyPasswordlessDefaults();
}
WebAuthnPolicyPasswordlessDefaults() {
super();
this.requireResidentKey = Constants.WEBAUTHN_POLICY_OPTION_YES;
this.userVerificationRequirement = Constants.WEBAUTHN_POLICY_OPTION_REQUIRED;
}
}

View File

@ -0,0 +1,113 @@
/*
* Copyright 2025 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.models;
import java.util.Collections;
import java.util.List;
import org.keycloak.storage.ReadOnlyException;
/**
* Default values for the WebAuthn configuration when used as Two Factor.
*
* @author rmartinc
*/
public class WebAuthnPolicyTwoFactorDefaults extends WebAuthnPolicy {
public static WebAuthnPolicy get() {
return new WebAuthnPolicyTwoFactorDefaults();
}
WebAuthnPolicyTwoFactorDefaults() {
this.rpEntityName = Constants.DEFAULT_WEBAUTHN_POLICY_RP_ENTITY_NAME;
this.signatureAlgorithms = List.of(Constants.DEFAULT_WEBAUTHN_POLICY_SIGNATURE_ALGORITHMS.split(","));
this.rpId = "";
this.attestationConveyancePreference = Constants.DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED;
this.authenticatorAttachment = Constants.DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED;
this.requireResidentKey = Constants.DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED;
this.userVerificationRequirement = Constants.DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED;
this.createTimeout = 0;
this.avoidSameAuthenticatorRegister = false;
this.acceptableAaguids = Collections.emptyList();
this.extraOrigins = Collections.emptyList();
this.passkeysEnabled = null;
}
@Override
public void setRpEntityName(String rpEntityName) {
throwReadOnlyException();
}
@Override
public void setSignatureAlgorithm(List<String> signatureAlgorithms) {
throwReadOnlyException();
}
@Override
public void setRpId(String rpId) {
throwReadOnlyException();
}
@Override
public void setAttestationConveyancePreference(String attestationConveyancePreference) {
throwReadOnlyException();
}
@Override
public void setAuthenticatorAttachment(String authenticatorAttachment) {
throwReadOnlyException();
}
@Override
public void setRequireResidentKey(String requireResidentKey) {
throwReadOnlyException();
}
@Override
public void setUserVerificationRequirement(String userVerificationRequirement) {
throwReadOnlyException();
}
@Override
public void setCreateTimeout(int createTimeout) {
throwReadOnlyException();
}
@Override
public void setAvoidSameAuthenticatorRegister(boolean avoidSameAuthenticatorRegister) {
throwReadOnlyException();
}
@Override
public void setAcceptableAaguids(List<String> acceptableAaguids) {
throwReadOnlyException();
}
@Override
public void setExtraOrigins(List<String> extraOrigins) {
throwReadOnlyException();
}
@Override
public void setPasskeysEnabled(Boolean passkeysEnabled) {
throwReadOnlyException();
}
private void throwReadOnlyException() {
throw new ReadOnlyException("Default WebAuthnPolicy!");
}
}

View File

@ -53,12 +53,6 @@ public interface WebAuthnConstants {
// key for storing onto AuthenticationSessionModel's Attribute challenge generated by RP(keycloak)
String AUTH_CHALLENGE_NOTE = "WEBAUTH_CHALLENGE";
// option values on WebAuth API
String OPTION_REQUIRED = "required";
String OPTION_PREFERED = "preferred";
String OPTION_DISCOURAGED = "discouraged";
String OPTION_NOT_SPECIFIED = "";
/* WebAuthn events */
// Event key for credential id generated by navigator.credentials.create()

View File

@ -43,6 +43,7 @@ import org.keycloak.events.Details;
import org.keycloak.events.Errors;
import org.keycloak.forms.login.LoginFormsProvider;
import org.keycloak.forms.login.freemarker.model.WebAuthnAuthenticatorsBean;
import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
@ -209,7 +210,7 @@ public class WebAuthnAuthenticator implements Authenticator, CredentialValidator
boolean isUVFlagChecked = false;
String userVerificationRequirement = getWebAuthnPolicy(context).getUserVerificationRequirement();
if (WebAuthnConstants.OPTION_REQUIRED.equals(userVerificationRequirement)) isUVFlagChecked = true;
if (Constants.WEBAUTHN_POLICY_OPTION_REQUIRED.equals(userVerificationRequirement)) isUVFlagChecked = true;
UserModel user = session.users().getUserById(context.getRealm(), userId);

View File

@ -245,7 +245,7 @@ public class WebAuthnRegister implements RequiredActionProvider, CredentialRegis
Challenge challenge = new DefaultChallenge(context.getAuthenticationSession().getAuthNote(WebAuthnConstants.AUTH_CHALLENGE_NOTE));
ServerProperty serverProperty = new ServerProperty(allOrigins, rpId, challenge, null);
// check User Verification by considering a malicious user might modify the result of calling WebAuthn API
boolean isUserVerificationRequired = policy.getUserVerificationRequirement().equals(WebAuthnConstants.OPTION_REQUIRED);
boolean isUserVerificationRequired = policy.getUserVerificationRequirement().equals(Constants.WEBAUTHN_POLICY_OPTION_REQUIRED);
final String transportsParam = params.getFirst(WebAuthnConstants.TRANSPORTS);

View File

@ -17,14 +17,13 @@
package org.keycloak.testsuite.webauthn.utils;
import org.keycloak.WebAuthnConstants;
import java.util.Arrays;
import org.keycloak.models.Constants;
public enum PropertyRequirement {
NOT_SPECIFIED(WebAuthnConstants.OPTION_NOT_SPECIFIED),
YES("Yes"),
NO("No");
NOT_SPECIFIED(Constants.DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED),
YES(Constants.WEBAUTHN_POLICY_OPTION_YES),
NO(Constants.WEBAUTHN_POLICY_OPTION_NO);
private final String value;

View File

@ -72,7 +72,7 @@ import static org.keycloak.testsuite.util.WaitUtils.waitForPageToLoad;
*/
public class AppInitiatedActionWebAuthnTest extends AbstractAppInitiatedActionTest implements UseVirtualAuthenticators {
private VirtualAuthenticatorManager virtualManager;
protected VirtualAuthenticatorManager virtualManager;
protected final String WEB_AUTHN_REGISTER_PROVIDER = isPasswordless() ? WebAuthnPasswordlessRegisterFactory.PROVIDER_ID : WebAuthnRegisterFactory.PROVIDER_ID;
protected final String DEFAULT_USERNAME = "test-user@localhost";

View File

@ -28,6 +28,7 @@ import org.keycloak.authentication.requiredactions.WebAuthnRegisterFactory;
import org.keycloak.common.util.SecretGenerator;
import org.keycloak.events.Details;
import org.keycloak.events.EventType;
import org.keycloak.models.Constants;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.credential.WebAuthnCredentialModel;
import org.keycloak.representations.idm.CredentialRepresentation;
@ -52,8 +53,6 @@ import java.util.stream.Collectors;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.keycloak.WebAuthnConstants.OPTION_DISCOURAGED;
import static org.keycloak.WebAuthnConstants.OPTION_REQUIRED;
import static org.keycloak.models.AuthenticationExecutionModel.Requirement.ALTERNATIVE;
import static org.keycloak.models.AuthenticationExecutionModel.Requirement.REQUIRED;
import static org.keycloak.testsuite.webauthn.utils.PropertyRequirement.NO;
@ -387,9 +386,9 @@ public class WebAuthnIdlessTest extends AbstractWebAuthnVirtualTest {
protected void setWebAuthnRealmSettings(boolean waRequireRK, boolean waRequireUV, boolean waplRequireRK, boolean waplRequireUV ) {
String waRequireRKString = waRequireRK ? YES.getValue() : NO.getValue();
String waRequireUVString = waRequireUV ? OPTION_REQUIRED : OPTION_DISCOURAGED;
String waRequireUVString = waRequireUV ? Constants.WEBAUTHN_POLICY_OPTION_REQUIRED : Constants.WEBAUTHN_POLICY_OPTION_DISCOURAGED;
String waplRequireRKString = waplRequireRK ? YES.getValue() : NO.getValue();
String waplRequireUVString = waplRequireUV ? OPTION_REQUIRED : OPTION_DISCOURAGED;
String waplRequireUVString = waplRequireUV ? Constants.WEBAUTHN_POLICY_OPTION_REQUIRED : Constants.WEBAUTHN_POLICY_OPTION_DISCOURAGED;
RealmRepresentation realmRep = testRealm().toRepresentation();

View File

@ -19,6 +19,7 @@ package org.keycloak.testsuite.webauthn;
import org.junit.Test;
import org.keycloak.WebAuthnConstants;
import org.keycloak.models.Constants;
import org.keycloak.models.credential.WebAuthnCredentialModel;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
@ -35,7 +36,6 @@ import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.keycloak.WebAuthnConstants.OPTION_REQUIRED;
import static org.keycloak.testsuite.webauthn.authenticators.DefaultVirtualAuthOptions.DEFAULT;
import static org.keycloak.testsuite.webauthn.authenticators.DefaultVirtualAuthOptions.DEFAULT_RESIDENT_KEY;
import static org.keycloak.testsuite.webauthn.utils.PropertyRequirement.YES;
@ -53,14 +53,14 @@ public class WebAuthnPropertyTest extends AbstractWebAuthnVirtualTest {
try (Closeable c = getWebAuthnRealmUpdater()
.setWebAuthnPolicyRpEntityName("localhost")
.setWebAuthnPolicyRequireResidentKey(YES.getValue())
.setWebAuthnPolicyUserVerificationRequirement(OPTION_REQUIRED)
.setWebAuthnPolicyUserVerificationRequirement(Constants.WEBAUTHN_POLICY_OPTION_REQUIRED)
.update()) {
WebAuthnRealmData realmData = new WebAuthnRealmData(testRealm().toRepresentation(), isPasswordless());
assertThat(realmData, notNullValue());
assertThat(realmData.getRpEntityName(), is("localhost"));
assertThat(realmData.getRequireResidentKey(), is(YES.getValue()));
assertThat(realmData.getUserVerificationRequirement(), is(OPTION_REQUIRED));
assertThat(realmData.getUserVerificationRequirement(), is(Constants.WEBAUTHN_POLICY_OPTION_REQUIRED));
registerDefaultUser();
@ -111,14 +111,14 @@ public class WebAuthnPropertyTest extends AbstractWebAuthnVirtualTest {
try (Closeable c = getWebAuthnRealmUpdater()
.setWebAuthnPolicyRpEntityName("localhost")
.setWebAuthnPolicyRequireResidentKey(YES.getValue())
.setWebAuthnPolicyUserVerificationRequirement(OPTION_REQUIRED)
.setWebAuthnPolicyUserVerificationRequirement(Constants.WEBAUTHN_POLICY_OPTION_REQUIRED)
.update()) {
WebAuthnRealmData realmData = new WebAuthnRealmData(testRealm().toRepresentation(), isPasswordless());
assertThat(realmData, notNullValue());
assertThat(realmData.getRpEntityName(), is("localhost"));
assertThat(realmData.getRequireResidentKey(), is(YES.getValue()));
assertThat(realmData.getUserVerificationRequirement(), is(OPTION_REQUIRED));
assertThat(realmData.getUserVerificationRequirement(), is(Constants.WEBAUTHN_POLICY_OPTION_REQUIRED));
registerDefaultUser();

View File

@ -98,6 +98,10 @@ public abstract class AbstractWebAuthnAccountTest extends AbstractAuthTest imple
}
}
public VirtualAuthenticatorManager getVirtualAuthManager() {
return webAuthnManager;
}
@Before
public void navigateBeforeTest() {
driver.manage().window().setSize(new Dimension(1920, 1080));

View File

@ -27,6 +27,7 @@ import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.RequiredActionProviderRepresentation;
import org.keycloak.testsuite.arquillian.annotation.IgnoreBrowserDriver;
import org.keycloak.testsuite.page.AbstractPatternFlyAlert;
import org.keycloak.testsuite.webauthn.authenticators.DefaultVirtualAuthOptions;
import org.keycloak.testsuite.webauthn.pages.SigningInPage;
import org.keycloak.testsuite.webauthn.pages.WebAuthnAuthenticatorsList;
import org.keycloak.testsuite.webauthn.updaters.AbstractWebAuthnRealmUpdater;
@ -87,12 +88,15 @@ public class WebAuthnSigningInTest extends AbstractWebAuthnAccountTest {
@Test
@IgnoreBrowserDriver(FirefoxDriver.class) // See https://github.com/keycloak/keycloak/issues/10368
public void passwordlessWebAuthnTest() {
getVirtualAuthManager().useAuthenticator(DefaultVirtualAuthOptions.DEFAULT_RESIDENT_KEY.getOptions());
testWebAuthn(true);
}
@Test
@IgnoreBrowserDriver(FirefoxDriver.class) // See https://github.com/keycloak/keycloak/issues/10368
public void createWebAuthnSameUserLabel() {
getVirtualAuthManager().useAuthenticator(DefaultVirtualAuthOptions.DEFAULT_RESIDENT_KEY.getOptions());
final String SAME_LABEL = "key123";
SigningInPage.UserCredential webAuthn = addWebAuthnCredential(SAME_LABEL, false);
@ -118,6 +122,8 @@ public class WebAuthnSigningInTest extends AbstractWebAuthnAccountTest {
@Test
@IgnoreBrowserDriver(FirefoxDriver.class) // See https://github.com/keycloak/keycloak/issues/10368
public void multipleSecurityKeys() {
getVirtualAuthManager().useAuthenticator(DefaultVirtualAuthOptions.DEFAULT_RESIDENT_KEY.getOptions());
final String LABEL = "SecurityKey#";
List<SigningInPage.UserCredential> createdCredentials = new ArrayList<>();
@ -175,6 +181,7 @@ public class WebAuthnSigningInTest extends AbstractWebAuthnAccountTest {
@Test
@IgnoreBrowserDriver(FirefoxDriver.class) // See https://github.com/keycloak/keycloak/issues/10368
public void avoidSameAuthenticatorRegisterPasswordless() throws IOException {
getVirtualAuthManager().useAuthenticator(DefaultVirtualAuthOptions.DEFAULT_RESIDENT_KEY.getOptions());
avoidSameAuthenticatorRegister(new PasswordLessRealmAttributeUpdater(testRealmResource()), webAuthnPwdlessCredentialType);
}
@ -212,6 +219,8 @@ public class WebAuthnSigningInTest extends AbstractWebAuthnAccountTest {
@Test
@IgnoreBrowserDriver(FirefoxDriver.class) // See https://github.com/keycloak/keycloak/issues/10368
public void notDisplayAvailableAuthenticatorsPasswordless() {
getVirtualAuthManager().useAuthenticator(DefaultVirtualAuthOptions.DEFAULT_RESIDENT_KEY.getOptions());
addWebAuthnCredential("authenticator#1", true);
addWebAuthnCredential("authenticator#2", true);

View File

@ -17,13 +17,25 @@
package org.keycloak.testsuite.webauthn.passwordless;
import org.junit.Before;
import org.keycloak.testsuite.util.BrowserDriverUtil;
import org.keycloak.testsuite.webauthn.AbstractWebAuthnVirtualTest;
import org.keycloak.testsuite.webauthn.AppInitiatedActionWebAuthnSkipIfExistsTest;
import org.keycloak.testsuite.webauthn.authenticators.DefaultVirtualAuthOptions;
/**
* @author rmartinc
*/
public class AppInitiatedActionPwdLessSkipIfExistsTest extends AppInitiatedActionWebAuthnSkipIfExistsTest {
@Before
@Override
public void setUpVirtualAuthenticator() {
if (!BrowserDriverUtil.isDriverFirefox(driver)) {
virtualManager = AbstractWebAuthnVirtualTest.createDefaultVirtualManager(driver, DefaultVirtualAuthOptions.DEFAULT_RESIDENT_KEY.getOptions());
}
}
@Override
protected boolean isPasswordless() {
return true;

View File

@ -17,13 +17,25 @@
package org.keycloak.testsuite.webauthn.passwordless;
import org.junit.Before;
import org.keycloak.testsuite.util.BrowserDriverUtil;
import org.keycloak.testsuite.webauthn.AbstractWebAuthnVirtualTest;
import org.keycloak.testsuite.webauthn.AppInitiatedActionWebAuthnTest;
import org.keycloak.testsuite.webauthn.authenticators.DefaultVirtualAuthOptions;
/**
* @author <a href="mailto:mabartos@redhat.com">Martin Bartos</a>
*/
public class AppInitiatedActionPwdLessTest extends AppInitiatedActionWebAuthnTest {
@Before
@Override
public void setUpVirtualAuthenticator() {
if (!BrowserDriverUtil.isDriverFirefox(driver)) {
virtualManager = AbstractWebAuthnVirtualTest.createDefaultVirtualManager(driver, DefaultVirtualAuthOptions.DEFAULT_RESIDENT_KEY.getOptions());
}
}
@Override
protected boolean isPasswordless() {
return true;

View File

@ -38,7 +38,6 @@ import org.keycloak.testsuite.pages.PageUtils;
import org.keycloak.testsuite.util.WaitUtils;
import org.keycloak.testsuite.webauthn.AbstractWebAuthnVirtualTest;
import org.keycloak.testsuite.webauthn.authenticators.DefaultVirtualAuthOptions;
import org.keycloak.testsuite.webauthn.utils.PropertyRequirement;
import org.openqa.selenium.firefox.FirefoxDriver;
/**
@ -72,11 +71,11 @@ public class PasskeysConditionalUITest extends AbstractWebAuthnVirtualTest {
// set passwordless policy for discoverable keys
try (Closeable c = getWebAuthnRealmUpdater()
.setWebAuthnPolicyRpEntityName("localhost")
.setWebAuthnPolicyRequireResidentKey(PropertyRequirement.YES.getValue())
.setWebAuthnPolicyUserVerificationRequirement(WebAuthnConstants.OPTION_REQUIRED)
.setWebAuthnPolicyRequireResidentKey(Constants.WEBAUTHN_POLICY_OPTION_YES)
.setWebAuthnPolicyUserVerificationRequirement(Constants.WEBAUTHN_POLICY_OPTION_REQUIRED)
.update()) {
checkWebAuthnConfiguration(PropertyRequirement.YES.getValue(), WebAuthnConstants.OPTION_REQUIRED);
checkWebAuthnConfiguration(Constants.WEBAUTHN_POLICY_OPTION_YES, Constants.WEBAUTHN_POLICY_OPTION_REQUIRED);
registerDefaultUser();
@ -107,8 +106,8 @@ public class PasskeysConditionalUITest extends AbstractWebAuthnVirtualTest {
// set passwordless policy not specified, key will not be discoverable
try (Closeable c = getWebAuthnRealmUpdater()
.setWebAuthnPolicyRpEntityName("localhost")
.setWebAuthnPolicyRequireResidentKey(PropertyRequirement.NOT_SPECIFIED.getValue())
.setWebAuthnPolicyUserVerificationRequirement(WebAuthnConstants.OPTION_NOT_SPECIFIED)
.setWebAuthnPolicyRequireResidentKey(Constants.DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED)
.setWebAuthnPolicyUserVerificationRequirement(Constants.DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED)
.update()) {
checkWebAuthnConfiguration(Constants.DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED, Constants.DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED);

View File

@ -28,6 +28,7 @@ import org.keycloak.common.Profile;
import org.keycloak.events.Details;
import org.keycloak.events.Errors;
import org.keycloak.events.EventType;
import org.keycloak.models.Constants;
import org.keycloak.models.credential.WebAuthnCredentialModel;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.organization.authentication.authenticators.browser.OrganizationAuthenticatorFactory;
@ -42,11 +43,9 @@ import org.keycloak.testsuite.admin.AbstractAdminTest;
import org.keycloak.testsuite.admin.ApiUtil;
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
import org.keycloak.testsuite.arquillian.annotation.IgnoreBrowserDriver;
import org.keycloak.testsuite.updaters.RealmAttributeUpdater;
import org.keycloak.testsuite.util.WaitUtils;
import org.keycloak.testsuite.webauthn.AbstractWebAuthnVirtualTest;
import org.keycloak.testsuite.webauthn.authenticators.DefaultVirtualAuthOptions;
import org.keycloak.testsuite.webauthn.utils.PropertyRequirement;
import org.openqa.selenium.By;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.firefox.FirefoxDriver;
@ -90,12 +89,12 @@ public class PasskeysOrganizationAuthenticationTest extends AbstractWebAuthnVirt
// set passwordless policy for discoverable keys
try (Closeable c = getWebAuthnRealmUpdater()
.setWebAuthnPolicyRpEntityName("localhost")
.setWebAuthnPolicyRequireResidentKey(PropertyRequirement.YES.getValue())
.setWebAuthnPolicyUserVerificationRequirement(WebAuthnConstants.OPTION_REQUIRED)
.setWebAuthnPolicyRequireResidentKey(Constants.WEBAUTHN_POLICY_OPTION_YES)
.setWebAuthnPolicyUserVerificationRequirement(Constants.WEBAUTHN_POLICY_OPTION_REQUIRED)
.setWebAuthnPolicyPasskeysEnabled(Boolean.TRUE)
.update()) {
checkWebAuthnConfiguration(PropertyRequirement.YES.getValue(), WebAuthnConstants.OPTION_REQUIRED);
checkWebAuthnConfiguration(Constants.WEBAUTHN_POLICY_OPTION_YES, Constants.WEBAUTHN_POLICY_OPTION_REQUIRED);
registerDefaultUser();
@ -141,12 +140,12 @@ public class PasskeysOrganizationAuthenticationTest extends AbstractWebAuthnVirt
// set passwordless policy for discoverable keys
try (Closeable c = getWebAuthnRealmUpdater()
.setWebAuthnPolicyRpEntityName("localhost")
.setWebAuthnPolicyRequireResidentKey(PropertyRequirement.YES.getValue())
.setWebAuthnPolicyUserVerificationRequirement(WebAuthnConstants.OPTION_REQUIRED)
.setWebAuthnPolicyRequireResidentKey(Constants.WEBAUTHN_POLICY_OPTION_YES)
.setWebAuthnPolicyUserVerificationRequirement(Constants.WEBAUTHN_POLICY_OPTION_REQUIRED)
.setWebAuthnPolicyPasskeysEnabled(Boolean.TRUE)
.update()) {
checkWebAuthnConfiguration(PropertyRequirement.YES.getValue(), WebAuthnConstants.OPTION_REQUIRED);
checkWebAuthnConfiguration(Constants.WEBAUTHN_POLICY_OPTION_YES, Constants.WEBAUTHN_POLICY_OPTION_REQUIRED);
registerDefaultUser();
@ -172,8 +171,8 @@ public class PasskeysOrganizationAuthenticationTest extends AbstractWebAuthnVirt
// set passwordless policy not specified, key will not be discoverable
try (Closeable c = getWebAuthnRealmUpdater()
.setWebAuthnPolicyRpEntityName("localhost")
.setWebAuthnPolicyRequireResidentKey(PropertyRequirement.NOT_SPECIFIED.getValue())
.setWebAuthnPolicyUserVerificationRequirement(WebAuthnConstants.OPTION_NOT_SPECIFIED)
.setWebAuthnPolicyRequireResidentKey(Constants.DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED)
.setWebAuthnPolicyUserVerificationRequirement(Constants.DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED)
.setWebAuthnPolicyPasskeysEnabled(Boolean.TRUE)
.update()) {
@ -227,12 +226,12 @@ public class PasskeysOrganizationAuthenticationTest extends AbstractWebAuthnVirt
// set passwordless policy for discoverable keys
try (Closeable c = getWebAuthnRealmUpdater()
.setWebAuthnPolicyRpEntityName("localhost")
.setWebAuthnPolicyRequireResidentKey(PropertyRequirement.YES.getValue())
.setWebAuthnPolicyUserVerificationRequirement(WebAuthnConstants.OPTION_REQUIRED)
.setWebAuthnPolicyRequireResidentKey(Constants.WEBAUTHN_POLICY_OPTION_YES)
.setWebAuthnPolicyUserVerificationRequirement(Constants.WEBAUTHN_POLICY_OPTION_REQUIRED)
.setWebAuthnPolicyPasskeysEnabled(Boolean.TRUE)
.update()) {
checkWebAuthnConfiguration(PropertyRequirement.YES.getValue(), WebAuthnConstants.OPTION_REQUIRED);
checkWebAuthnConfiguration(Constants.WEBAUTHN_POLICY_OPTION_YES, Constants.WEBAUTHN_POLICY_OPTION_REQUIRED);
registerDefaultUser();

View File

@ -31,6 +31,7 @@ import org.keycloak.events.Details;
import org.keycloak.events.Errors;
import org.keycloak.events.EventType;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.Constants;
import org.keycloak.models.credential.PasswordCredentialModel;
import org.keycloak.models.credential.WebAuthnCredentialModel;
import org.keycloak.representations.idm.AuthenticationExecutionExportRepresentation;
@ -45,7 +46,6 @@ import org.keycloak.testsuite.arquillian.annotation.IgnoreBrowserDriver;
import org.keycloak.testsuite.util.WaitUtils;
import org.keycloak.testsuite.webauthn.AbstractWebAuthnVirtualTest;
import org.keycloak.testsuite.webauthn.authenticators.DefaultVirtualAuthOptions;
import org.keycloak.testsuite.webauthn.utils.PropertyRequirement;
import org.openqa.selenium.By;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.firefox.FirefoxDriver;
@ -114,12 +114,12 @@ public class PasskeysUsernameFormTest extends AbstractWebAuthnVirtualTest {
// set passwordless policy for discoverable keys
try (Closeable c = getWebAuthnRealmUpdater()
.setWebAuthnPolicyRpEntityName("localhost")
.setWebAuthnPolicyRequireResidentKey(PropertyRequirement.YES.getValue())
.setWebAuthnPolicyUserVerificationRequirement(WebAuthnConstants.OPTION_REQUIRED)
.setWebAuthnPolicyRequireResidentKey(Constants.WEBAUTHN_POLICY_OPTION_YES)
.setWebAuthnPolicyUserVerificationRequirement(Constants.WEBAUTHN_POLICY_OPTION_REQUIRED)
.setWebAuthnPolicyPasskeysEnabled(Boolean.TRUE)
.update()) {
checkWebAuthnConfiguration(PropertyRequirement.YES.getValue(), WebAuthnConstants.OPTION_REQUIRED);
checkWebAuthnConfiguration(Constants.WEBAUTHN_POLICY_OPTION_YES, Constants.WEBAUTHN_POLICY_OPTION_REQUIRED);
registerDefaultUser();
@ -162,8 +162,8 @@ public class PasskeysUsernameFormTest extends AbstractWebAuthnVirtualTest {
// set passwordless policy not specified, key will not be discoverable
try (Closeable c = getWebAuthnRealmUpdater()
.setWebAuthnPolicyRpEntityName("localhost")
.setWebAuthnPolicyRequireResidentKey(PropertyRequirement.NOT_SPECIFIED.getValue())
.setWebAuthnPolicyUserVerificationRequirement(WebAuthnConstants.OPTION_NOT_SPECIFIED)
.setWebAuthnPolicyRequireResidentKey(Constants.DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED)
.setWebAuthnPolicyUserVerificationRequirement(Constants.DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED)
.setWebAuthnPolicyPasskeysEnabled(Boolean.TRUE)
.update()) {
registerDefaultUser();
@ -217,12 +217,12 @@ public class PasskeysUsernameFormTest extends AbstractWebAuthnVirtualTest {
// set passwordless policy for discoverable keys
try (Closeable c = getWebAuthnRealmUpdater()
.setWebAuthnPolicyRpEntityName("localhost")
.setWebAuthnPolicyRequireResidentKey(PropertyRequirement.YES.getValue())
.setWebAuthnPolicyUserVerificationRequirement(WebAuthnConstants.OPTION_REQUIRED)
.setWebAuthnPolicyRequireResidentKey(Constants.WEBAUTHN_POLICY_OPTION_YES)
.setWebAuthnPolicyUserVerificationRequirement(Constants.WEBAUTHN_POLICY_OPTION_REQUIRED)
.setWebAuthnPolicyPasskeysEnabled(Boolean.TRUE)
.update()) {
checkWebAuthnConfiguration(PropertyRequirement.YES.getValue(), WebAuthnConstants.OPTION_REQUIRED);
checkWebAuthnConfiguration(Constants.WEBAUTHN_POLICY_OPTION_YES, Constants.WEBAUTHN_POLICY_OPTION_REQUIRED);
registerDefaultUser();

View File

@ -30,6 +30,7 @@ import org.keycloak.common.Profile;
import org.keycloak.events.Details;
import org.keycloak.events.Errors;
import org.keycloak.events.EventType;
import org.keycloak.models.Constants;
import org.keycloak.models.credential.WebAuthnCredentialModel;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
@ -40,7 +41,6 @@ import org.keycloak.testsuite.pages.SelectOrganizationPage;
import org.keycloak.testsuite.util.WaitUtils;
import org.keycloak.testsuite.webauthn.AbstractWebAuthnVirtualTest;
import org.keycloak.testsuite.webauthn.authenticators.DefaultVirtualAuthOptions;
import org.keycloak.testsuite.webauthn.utils.PropertyRequirement;
import org.openqa.selenium.By;
import org.openqa.selenium.firefox.FirefoxDriver;
@ -77,12 +77,12 @@ public class PasskeysUsernamePasswordFormTest extends AbstractWebAuthnVirtualTes
// set passwordless policy for discoverable keys
try (Closeable c = getWebAuthnRealmUpdater()
.setWebAuthnPolicyRpEntityName("localhost")
.setWebAuthnPolicyRequireResidentKey(PropertyRequirement.YES.getValue())
.setWebAuthnPolicyUserVerificationRequirement(WebAuthnConstants.OPTION_REQUIRED)
.setWebAuthnPolicyRequireResidentKey(null)
.setWebAuthnPolicyUserVerificationRequirement(null)
.setWebAuthnPolicyPasskeysEnabled(Boolean.TRUE)
.update()) {
checkWebAuthnConfiguration(PropertyRequirement.YES.getValue(), WebAuthnConstants.OPTION_REQUIRED);
checkWebAuthnConfiguration(Constants.WEBAUTHN_POLICY_OPTION_YES, Constants.WEBAUTHN_POLICY_OPTION_REQUIRED);
registerDefaultUser();
@ -116,8 +116,8 @@ public class PasskeysUsernamePasswordFormTest extends AbstractWebAuthnVirtualTes
// set passwordless policy not specified, key will not be discoverable
try (Closeable c = getWebAuthnRealmUpdater()
.setWebAuthnPolicyRpEntityName("localhost")
.setWebAuthnPolicyRequireResidentKey(PropertyRequirement.NOT_SPECIFIED.getValue())
.setWebAuthnPolicyUserVerificationRequirement(WebAuthnConstants.OPTION_NOT_SPECIFIED)
.setWebAuthnPolicyRequireResidentKey(Constants.DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED)
.setWebAuthnPolicyUserVerificationRequirement(Constants.DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED)
.setWebAuthnPolicyPasskeysEnabled(Boolean.TRUE)
.update()) {
registerDefaultUser();
@ -166,12 +166,12 @@ public class PasskeysUsernamePasswordFormTest extends AbstractWebAuthnVirtualTes
// set passwordless policy for discoverable keys
try (Closeable c = getWebAuthnRealmUpdater()
.setWebAuthnPolicyRpEntityName("localhost")
.setWebAuthnPolicyRequireResidentKey(PropertyRequirement.YES.getValue())
.setWebAuthnPolicyUserVerificationRequirement(WebAuthnConstants.OPTION_REQUIRED)
.setWebAuthnPolicyRequireResidentKey(Constants.WEBAUTHN_POLICY_OPTION_YES)
.setWebAuthnPolicyUserVerificationRequirement(Constants.WEBAUTHN_POLICY_OPTION_REQUIRED)
.setWebAuthnPolicyPasskeysEnabled(Boolean.TRUE)
.update()) {
checkWebAuthnConfiguration(PropertyRequirement.YES.getValue(), WebAuthnConstants.OPTION_REQUIRED);
checkWebAuthnConfiguration(Constants.WEBAUTHN_POLICY_OPTION_YES, Constants.WEBAUTHN_POLICY_OPTION_REQUIRED);
registerDefaultUser();

View File

@ -35,8 +35,8 @@ import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.keycloak.WebAuthnConstants.OPTION_REQUIRED;
import static org.keycloak.models.Constants.DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED;
import static org.keycloak.models.Constants.WEBAUTHN_POLICY_OPTION_REQUIRED;
import static org.keycloak.testsuite.webauthn.authenticators.DefaultVirtualAuthOptions.DEFAULT_RESIDENT_KEY;
/**
@ -70,7 +70,7 @@ public class ResidentKeyRegisterTest extends AbstractWebAuthnVirtualTest {
if (hasResidentKey) {
getVirtualAuthManager().useAuthenticator(DEFAULT_RESIDENT_KEY.getOptions());
userVerification = OPTION_REQUIRED;
userVerification = WEBAUTHN_POLICY_OPTION_REQUIRED;
} else {
userVerification = DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED;
}