Browse Source

Add MultiFactorCondition.WEBAUTHN_REGISTERED

Closes gh-18923
pull/18814/merge
Robert Winch 2 weeks ago
parent
commit
ea2f2302da
No known key found for this signature in database
  1. 19
      config/src/main/java/org/springframework/security/config/annotation/authorization/EnableMultiFactorAuthentication.java
  2. 20
      config/src/main/java/org/springframework/security/config/annotation/authorization/MultiFactorAuthenticationSelector.java
  3. 58
      config/src/main/java/org/springframework/security/config/annotation/authorization/MultiFactorCondition.java
  4. 90
      config/src/main/java/org/springframework/security/config/annotation/authorization/WhenWebAuthnRegisteredMfaConfiguration.java
  5. 108
      config/src/test/java/org/springframework/security/config/annotation/authorization/MultiFactorAuthenticationSelectorTests.java
  6. 13
      docs/modules/ROOT/pages/servlet/authentication/mfa.adoc
  7. 1
      docs/modules/ROOT/pages/whats-new.adoc
  8. 1
      docs/spring-security-docs.gradle
  9. 18
      docs/src/test/java/org/springframework/security/docs/servlet/authentication/mfawhencustomconditions/CustomizerAuthorizationManagerFactoryConfiguration.java
  10. 37
      docs/src/test/java/org/springframework/security/docs/servlet/authentication/mfawhenwebauthnregistered/WebAuthnConditionConfiguration.java
  11. 18
      docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authentication/mfawhencustomconditions/CustomizerAuthorizationManagerFactoryConfiguration.kt
  12. 37
      docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authentication/mfawhenwebauthnregistered/WebAuthnConditionConfiguration.kt

19
config/src/main/java/org/springframework/security/config/annotation/authorization/EnableMultiFactorAuthentication.java

@ -30,8 +30,12 @@ import org.springframework.security.authorization.DefaultAuthorizationManagerFac @@ -30,8 +30,12 @@ import org.springframework.security.authorization.DefaultAuthorizationManagerFac
*
* When {@link #authorities()} is specified creates a
* {@link DefaultAuthorizationManagerFactory} as a Bean with the {@link #authorities()}
* specified as additional required authorities. The configuration will be picked up by
* both
* specified as additional required authorities. When {@link #when()} is
* {@link MultiFactorCondition#WEBAUTHN_REGISTERED}, {@link #authorities()} must include
* {@link org.springframework.security.core.authority.FactorGrantedAuthority#WEBAUTHN_AUTHORITY};
* otherwise an {@link IllegalArgumentException} is thrown during configuration
* processing. When {@link #when()} is not specified (default is an empty array), no such
* requirement applies. The configuration will be picked up by both
* {@link org.springframework.security.config.annotation.web.configuration.EnableWebSecurity}
* and
* {@link org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity}.
@ -80,4 +84,15 @@ public @interface EnableMultiFactorAuthentication { @@ -80,4 +84,15 @@ public @interface EnableMultiFactorAuthentication {
*/
String[] authorities();
/**
* The conditions under which multi-factor authentication is required.
* <p>
* When multiple conditions are specified, they are applied as an AND (all conditions
* must be met).
* @return the conditions (default is an empty array, which requires MFA
* unconditionally)
* @since 7.1
*/
MultiFactorCondition[] when() default {};
}

20
config/src/main/java/org/springframework/security/config/annotation/authorization/MultiFactorAuthenticationSelector.java

@ -17,16 +17,24 @@ @@ -17,16 +17,24 @@
package org.springframework.security.config.annotation.authorization;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.security.authorization.DefaultAuthorizationManagerFactory;
import org.springframework.security.core.authority.FactorGrantedAuthority;
/**
* Uses {@link EnableMultiFactorAuthentication} to configure a
* {@link DefaultAuthorizationManagerFactory}.
* <p>
* When {@link EnableMultiFactorAuthentication#when()} includes
* {@link MultiFactorCondition#WEBAUTHN_REGISTERED}, validates that
* {@link EnableMultiFactorAuthentication#authorities()} includes
* {@link org.springframework.security.core.authority.FactorGrantedAuthority#WEBAUTHN_AUTHORITY}
* and throws an {@link IllegalArgumentException} if not.
*
* @author Rob Winch
* @since 7.0
@ -39,9 +47,19 @@ class MultiFactorAuthenticationSelector implements ImportSelector { @@ -39,9 +47,19 @@ class MultiFactorAuthenticationSelector implements ImportSelector {
Map<String, Object> multiFactorAuthenticationAttrs = metadata
.getAnnotationAttributes(EnableMultiFactorAuthentication.class.getName());
String[] authorities = (String[]) multiFactorAuthenticationAttrs.getOrDefault("authorities", new String[0]);
List<String> imports = new ArrayList<>(2);
MultiFactorCondition[] when = (MultiFactorCondition[]) multiFactorAuthenticationAttrs.getOrDefault("when",
new MultiFactorCondition[0]);
boolean hasWebAuthn = Arrays.asList(when).contains(MultiFactorCondition.WEBAUTHN_REGISTERED);
if (hasWebAuthn && !Arrays.asList(authorities).contains(FactorGrantedAuthority.WEBAUTHN_AUTHORITY)) {
throw new IllegalArgumentException("When when() includes " + MultiFactorCondition.WEBAUTHN_REGISTERED
+ ", authorities() must include " + FactorGrantedAuthority.WEBAUTHN_AUTHORITY);
}
List<String> imports = new ArrayList<>(3);
if (authorities.length > 0) {
imports.add(AuthorizationManagerFactoryConfiguration.class.getName());
if (hasWebAuthn) {
imports.add(WhenWebAuthnRegisteredMfaConfiguration.class.getName());
}
}
imports.add(EnableMfaFiltersConfiguration.class.getName());
return imports.toArray(new String[imports.size()]);

58
config/src/main/java/org/springframework/security/config/annotation/authorization/MultiFactorCondition.java

@ -0,0 +1,58 @@ @@ -0,0 +1,58 @@
/*
* Copyright 2004-present the original author or authors.
*
* 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
*
* https://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.springframework.security.config.annotation.authorization;
/**
* Condition under which multi-factor authentication is required.
*
* @author Rob Winch
* @since 7.1
* @see EnableMultiFactorAuthentication#when()
*/
public enum MultiFactorCondition {
/**
* Require multi-factor authentication only when the user has a WebAuthn credential
* record registered.
* <p>
* When this condition is specified,
* {@link EnableMultiFactorAuthentication#authorities()} must include
* {@link org.springframework.security.core.authority.FactorGrantedAuthority#WEBAUTHN_AUTHORITY}.
* Failing to include it results in an {@link IllegalArgumentException} when the
* configuration is processed.
* <p>
* Using this condition also requires both a
* {@link org.springframework.security.web.webauthn.management.PublicKeyCredentialUserEntityRepository}
* Bean and a
* {@link org.springframework.security.web.webauthn.management.UserCredentialRepository}
* Bean to be published.
*
* <pre>
* &#64;Bean
* public PublicKeyCredentialUserEntityRepository userEntityRepository() {
* return new InMemoryPublicKeyCredentialUserEntityRepository();
* }
*
* &#64;Bean
* public UserCredentialRepository userCredentialRepository() {
* return new InMemoryUserCredentialRepository();
* }
* </pre>
*/
WEBAUTHN_REGISTERED
}

90
config/src/main/java/org/springframework/security/config/annotation/authorization/WhenWebAuthnRegisteredMfaConfiguration.java

@ -0,0 +1,90 @@ @@ -0,0 +1,90 @@
/*
* Copyright 2004-present the original author or authors.
*
* 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
*
* https://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.springframework.security.config.annotation.authorization;
import java.util.function.Predicate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authorization.AuthorizationManagerFactories.AdditionalRequiredFactorsBuilder;
import org.springframework.security.config.Customizer;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity;
import org.springframework.security.web.webauthn.management.PublicKeyCredentialUserEntityRepository;
import org.springframework.security.web.webauthn.management.UserCredentialRepository;
/**
* Configuration that provides a
* {@link Customizer}&lt;{@link AdditionalRequiredFactorsBuilder}&gt; for
* {@link MultiFactorCondition#WEBAUTHN_REGISTERED}, requiring multi-factor authentication
* only when the user has a WebAuthn credential record.
*
* @author Rob Winch
* @since 7.1
* @see EnableMultiFactorAuthentication#when()
* @see MultiFactorCondition#WEBAUTHN_REGISTERED
*/
@Configuration(proxyBeanMethods = false)
class WhenWebAuthnRegisteredMfaConfiguration {
@Bean
Customizer<AdditionalRequiredFactorsBuilder<Object>> additionalRequiredFactorsCustomizer(
PublicKeyCredentialUserEntityRepository userEntityRepository,
UserCredentialRepository userCredentialRepository) {
return (builder) -> builder.withWhen((current) -> {
Predicate<Authentication> webAuthnRegisteredPredicate = new WebAuthnRegisteredPredicate(
userEntityRepository, userCredentialRepository);
if (current == null) {
return webAuthnRegisteredPredicate;
}
return current.and(webAuthnRegisteredPredicate);
});
}
private static final class WebAuthnRegisteredPredicate implements Predicate<Authentication> {
private final PublicKeyCredentialUserEntityRepository userEntityRepository;
private final UserCredentialRepository userCredentialRepository;
private WebAuthnRegisteredPredicate(PublicKeyCredentialUserEntityRepository userEntityRepository,
UserCredentialRepository userCredentialRepository) {
this.userEntityRepository = userEntityRepository;
this.userCredentialRepository = userCredentialRepository;
}
@Override
public boolean test(Authentication authentication) {
if (authentication == null || authentication.getName() == null) {
return false;
}
PublicKeyCredentialUserEntity userEntity = this.userEntityRepository
.findByUsername(authentication.getName());
if (userEntity == null) {
return false;
}
return !this.userCredentialRepository.findByUserId(userEntity.getId()).isEmpty();
}
@Override
public String toString() {
return "WEBAUTHN_REGISTERED";
}
}
}

108
config/src/test/java/org/springframework/security/config/annotation/authorization/MultiFactorAuthenticationSelectorTests.java

@ -0,0 +1,108 @@ @@ -0,0 +1,108 @@
/*
* Copyright 2004-present the original author or authors.
*
* 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
*
* https://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.springframework.security.config.annotation.authorization;
import java.util.HashMap;
import java.util.Map;
import org.junit.jupiter.api.Test;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.security.core.authority.FactorGrantedAuthority;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link MultiFactorAuthenticationSelector}.
*
* @author Rob Winch
*/
class MultiFactorAuthenticationSelectorTests {
private final MultiFactorAuthenticationSelector selector = new MultiFactorAuthenticationSelector();
@Test
void selectImportsWhenWhenIsEmptyAndAuthoritiesSpecifiedThenReturnsImportsWithoutWebAuthnConfig() {
AnnotationMetadata metadata = metadata(new MultiFactorCondition[0], FactorGrantedAuthority.OTT_AUTHORITY,
FactorGrantedAuthority.PASSWORD_AUTHORITY);
String[] imports = this.selector.selectImports(metadata);
assertThat(imports).isNotEmpty();
assertThat(imports).doesNotContain(WhenWebAuthnRegisteredMfaConfiguration.class.getName());
}
@Test
void selectImportsWhenWhenOmittedThenDefaultsToEmptyAndReturnsImports() {
AnnotationMetadata metadata = metadataWithoutWhen(FactorGrantedAuthority.OTT_AUTHORITY,
FactorGrantedAuthority.PASSWORD_AUTHORITY);
String[] imports = this.selector.selectImports(metadata);
assertThat(imports).isNotEmpty();
assertThat(imports).doesNotContain(WhenWebAuthnRegisteredMfaConfiguration.class.getName());
}
@Test
void selectImportsWhenHasWebAuthnConditionAndAuthoritiesIncludesWebAuthnThenReturnsImports() {
AnnotationMetadata metadata = metadata(new MultiFactorCondition[] { MultiFactorCondition.WEBAUTHN_REGISTERED },
FactorGrantedAuthority.OTT_AUTHORITY, FactorGrantedAuthority.PASSWORD_AUTHORITY,
FactorGrantedAuthority.WEBAUTHN_AUTHORITY);
String[] imports = this.selector.selectImports(metadata);
assertThat(imports).isNotEmpty();
}
@Test
void selectImportsWhenHasWebAuthnConditionAndAuthoritiesOnlyWebAuthnThenReturnsImports() {
AnnotationMetadata metadata = metadata(new MultiFactorCondition[] { MultiFactorCondition.WEBAUTHN_REGISTERED },
FactorGrantedAuthority.WEBAUTHN_AUTHORITY);
String[] imports = this.selector.selectImports(metadata);
assertThat(imports).isNotEmpty();
}
@Test
void selectImportsWhenHasWebAuthnConditionAndAuthoritiesEmptyThenThrowsException() {
AnnotationMetadata metadata = metadata(new MultiFactorCondition[] { MultiFactorCondition.WEBAUTHN_REGISTERED });
assertThatIllegalArgumentException().isThrownBy(() -> this.selector.selectImports(metadata))
.withMessageContaining("authorities() must include " + FactorGrantedAuthority.WEBAUTHN_AUTHORITY);
}
@Test
void selectImportsWhenHasWebAuthnConditionAndAuthoritiesExcludesWebAuthnThenThrowsException() {
AnnotationMetadata metadata = metadata(new MultiFactorCondition[] { MultiFactorCondition.WEBAUTHN_REGISTERED },
FactorGrantedAuthority.OTT_AUTHORITY, FactorGrantedAuthority.PASSWORD_AUTHORITY);
assertThatIllegalArgumentException().isThrownBy(() -> this.selector.selectImports(metadata))
.withMessageContaining("authorities() must include " + FactorGrantedAuthority.WEBAUTHN_AUTHORITY);
}
private static AnnotationMetadata metadata(MultiFactorCondition[] when, String... authorities) {
AnnotationMetadata metadata = mock(AnnotationMetadata.class);
Map<String, Object> attrs = new HashMap<>();
attrs.put("authorities", authorities);
attrs.put("when", when);
given(metadata.getAnnotationAttributes(EnableMultiFactorAuthentication.class.getName())).willReturn(attrs);
return metadata;
}
private static AnnotationMetadata metadataWithoutWhen(String... authorities) {
AnnotationMetadata metadata = mock(AnnotationMetadata.class);
Map<String, Object> attrs = new HashMap<>();
attrs.put("authorities", authorities);
given(metadata.getAnnotationAttributes(EnableMultiFactorAuthentication.class.getName())).willReturn(attrs);
return metadata;
}
}

13
docs/modules/ROOT/pages/servlet/authentication/mfa.adoc

@ -37,6 +37,19 @@ Spring Security behind the scenes knows which endpoint to go to depending on whi @@ -37,6 +37,19 @@ Spring Security behind the scenes knows which endpoint to go to depending on whi
If the user logged in initially with their username and password, then Spring Security redirects to the One-Time-Token Login page.
If the user logged in initially with a token, then Spring Security redirects to the Username/Password Login page.
[[mfa-when-webauthn-registered]]
=== Conditionally Requiring MFA for WebAuthn Users
At times, you may want to conditionally require MFA only for users who have registered a WebAuthn credential (passkey).
You can achieve this by specifying javadoc:org.springframework.security.config.annotation.authorization.EnableMultiFactorAuthentication#when()[when = MultiFactorCondition.WEBAUTHN_REGISTERED].
include-code::./WebAuthnConditionConfiguration[tag=enable-mfa-webauthn,indent=0]
This configuration requires `FACTOR_WEBAUTHN` and `FACTOR_PASSWORD` only when the user has registered a passkey.
It works by publishing a xref:./mfa.adoc#mfa-when-custom-conditions[`Customizer<AdditionalRequiredFactorsBuilder<Object>>`] that updates the xref:./mfa.adoc#programmatic-mfa[`withWhen`] method with the condition.
NOTE: This condition requires both a javadoc:org.springframework.security.web.webauthn.management.PublicKeyCredentialUserEntityRepository[] bean and a javadoc:org.springframework.security.web.webauthn.management.UserCredentialRepository[] bean to be published in order to determine if the user has registered a WebAuthn credential.
[[mfa-when-custom-conditions]]
=== Custom MFA Conditions

1
docs/modules/ROOT/pages/whats-new.adoc

@ -7,6 +7,7 @@ @@ -7,6 +7,7 @@
* https://github.com/spring-projects/spring-security/issues/18755[gh-18755] - Include `charset` in `WWW-Authenticate` header
* Added xref:servlet/authorization/architecture.adoc#authz-conditional-authorization-manager[ConditionalAuthorizationManager]
* Added `when` and `withWhen` conditions to `AuthorizationManagerFactories.multiFactor()` for xref:servlet/authentication/mfa.adoc#programmatic-mfa[Programmatic MFA]
* Added `MultiFactorCondition.WEBAUTHN_REGISTERED` to `@EnableMultiFactorAuthentication(when = ...)` for xref:servlet/authentication/mfa.adoc#mfa-when-webauthn-registered[conditionally requiring MFA for WebAuthn Users]
== OAuth 2.0

1
docs/spring-security-docs.gradle

@ -44,6 +44,7 @@ dependencies { @@ -44,6 +44,7 @@ dependencies {
testImplementation project(':spring-security-oauth2-client')
testImplementation project(':spring-security-oauth2-resource-server')
testImplementation project(':spring-security-messaging')
testImplementation project(':spring-security-webauthn')
testImplementation 'com.squareup.okhttp3:mockwebserver'
testImplementation libs.com.password4j.password4j
testImplementation 'com.unboundid:unboundid-ldapsdk'

18
docs/src/test/java/org/springframework/security/docs/servlet/authentication/mfawhencustomconditions/CustomizerAuthorizationManagerFactoryConfiguration.java

@ -0,0 +1,18 @@ @@ -0,0 +1,18 @@
package org.springframework.security.docs.servlet.authentication.mfawhencustomconditions;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authorization.AuthorizationManagerFactories;
import org.springframework.security.config.Customizer;
@Configuration(proxyBeanMethods = false)
class CustomizerAuthorizationManagerFactoryConfiguration {
// tag::customizer[]
@Bean
Customizer<AuthorizationManagerFactories.AdditionalRequiredFactorsBuilder<Object>> additionalRequiredFactorsCustomizer() {
return (builder) -> builder.when((auth) -> "admin".equals(auth.getName()));
}
// end::customizer[]
}

37
docs/src/test/java/org/springframework/security/docs/servlet/authentication/mfawhenwebauthnregistered/WebAuthnConditionConfiguration.java

@ -0,0 +1,37 @@ @@ -0,0 +1,37 @@
package org.springframework.security.docs.servlet.authentication.mfawhenwebauthnregistered;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authorization.EnableMultiFactorAuthentication;
import org.springframework.security.config.annotation.authorization.MultiFactorCondition;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.authority.FactorGrantedAuthority;
import org.springframework.security.web.webauthn.management.MapPublicKeyCredentialUserEntityRepository;
import org.springframework.security.web.webauthn.management.MapUserCredentialRepository;
import org.springframework.security.web.webauthn.management.PublicKeyCredentialUserEntityRepository;
import org.springframework.security.web.webauthn.management.UserCredentialRepository;
@EnableWebSecurity
@Configuration(proxyBeanMethods = false)
// tag::enable-mfa-webauthn[]
@EnableMultiFactorAuthentication(
authorities = {
FactorGrantedAuthority.PASSWORD_AUTHORITY,
FactorGrantedAuthority.WEBAUTHN_AUTHORITY
},
when = MultiFactorCondition.WEBAUTHN_REGISTERED
)
public class WebAuthnConditionConfiguration {
@Bean
public PublicKeyCredentialUserEntityRepository userEntityRepository() {
return new MapPublicKeyCredentialUserEntityRepository();
}
@Bean
public UserCredentialRepository userCredentialRepository() {
return new MapUserCredentialRepository();
}
}
// end::enable-mfa-webauthn[]

18
docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authentication/mfawhencustomconditions/CustomizerAuthorizationManagerFactoryConfiguration.kt

@ -0,0 +1,18 @@ @@ -0,0 +1,18 @@
package org.springframework.security.kt.docs.servlet.authentication.mfawhencustomconditions
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.authorization.AuthorizationManagerFactories
import org.springframework.security.config.Customizer
@Configuration(proxyBeanMethods = false)
internal class CustomizerAuthorizationManagerFactoryConfiguration {
// tag::customizer[]
@Bean
fun additionalRequiredFactorsCustomizer(): Customizer<AuthorizationManagerFactories.AdditionalRequiredFactorsBuilder<Any>> {
return Customizer { builder -> builder.`when` { auth -> "admin" == auth.name } }
}
// end::customizer[]
}

37
docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authentication/mfawhenwebauthnregistered/WebAuthnConditionConfiguration.kt

@ -0,0 +1,37 @@ @@ -0,0 +1,37 @@
package org.springframework.security.kt.docs.servlet.authentication.mfawhenwebauthnregistered
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.config.annotation.authorization.EnableMultiFactorAuthentication
import org.springframework.security.config.annotation.authorization.MultiFactorCondition
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.core.authority.FactorGrantedAuthority
import org.springframework.security.web.webauthn.management.MapPublicKeyCredentialUserEntityRepository
import org.springframework.security.web.webauthn.management.MapUserCredentialRepository
import org.springframework.security.web.webauthn.management.PublicKeyCredentialUserEntityRepository
import org.springframework.security.web.webauthn.management.UserCredentialRepository
@EnableWebSecurity
@Configuration(proxyBeanMethods = false)
// tag::enable-mfa-webauthn[]
@EnableMultiFactorAuthentication(
authorities = [
FactorGrantedAuthority.PASSWORD_AUTHORITY,
FactorGrantedAuthority.WEBAUTHN_AUTHORITY
],
`when` = [MultiFactorCondition.WEBAUTHN_REGISTERED]
)
internal class WebAuthnConditionConfiguration {
@Bean
fun userEntityRepository(): PublicKeyCredentialUserEntityRepository {
return MapPublicKeyCredentialUserEntityRepository()
}
@Bean
fun userCredentialRepository(): UserCredentialRepository {
return MapUserCredentialRepository()
}
}
// end::enable-mfa-webauthn[]
Loading…
Cancel
Save