Set PublicKeyCredentialCreationOptionsRepository by DSL or Bean
Closes gh-16396
This commit is contained in:
commit
683f1f4bc5
|
@ -44,6 +44,7 @@ import org.springframework.security.web.webauthn.management.WebAuthnRelyingParty
|
||||||
import org.springframework.security.web.webauthn.management.Webauthn4JRelyingPartyOperations;
|
import org.springframework.security.web.webauthn.management.Webauthn4JRelyingPartyOperations;
|
||||||
import org.springframework.security.web.webauthn.registration.DefaultWebAuthnRegistrationPageGeneratingFilter;
|
import org.springframework.security.web.webauthn.registration.DefaultWebAuthnRegistrationPageGeneratingFilter;
|
||||||
import org.springframework.security.web.webauthn.registration.PublicKeyCredentialCreationOptionsFilter;
|
import org.springframework.security.web.webauthn.registration.PublicKeyCredentialCreationOptionsFilter;
|
||||||
|
import org.springframework.security.web.webauthn.registration.PublicKeyCredentialCreationOptionsRepository;
|
||||||
import org.springframework.security.web.webauthn.registration.WebAuthnRegistrationFilter;
|
import org.springframework.security.web.webauthn.registration.WebAuthnRegistrationFilter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -64,6 +65,8 @@ public class WebAuthnConfigurer<H extends HttpSecurityBuilder<H>>
|
||||||
|
|
||||||
private boolean disableDefaultRegistrationPage = false;
|
private boolean disableDefaultRegistrationPage = false;
|
||||||
|
|
||||||
|
private PublicKeyCredentialCreationOptionsRepository creationOptionsRepository;
|
||||||
|
|
||||||
private HttpMessageConverter<Object> converter;
|
private HttpMessageConverter<Object> converter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -130,6 +133,17 @@ public class WebAuthnConfigurer<H extends HttpSecurityBuilder<H>>
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets PublicKeyCredentialCreationOptionsRepository
|
||||||
|
* @param creationOptionsRepository the creationOptionsRepository
|
||||||
|
* @return the {@link WebAuthnConfigurer} for further customization
|
||||||
|
*/
|
||||||
|
public WebAuthnConfigurer<H> creationOptionsRepository(
|
||||||
|
PublicKeyCredentialCreationOptionsRepository creationOptionsRepository) {
|
||||||
|
this.creationOptionsRepository = creationOptionsRepository;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void configure(H http) throws Exception {
|
public void configure(H http) throws Exception {
|
||||||
UserDetailsService userDetailsService = getSharedOrBean(http, UserDetailsService.class).orElseGet(() -> {
|
UserDetailsService userDetailsService = getSharedOrBean(http, UserDetailsService.class).orElseGet(() -> {
|
||||||
|
@ -141,6 +155,7 @@ public class WebAuthnConfigurer<H extends HttpSecurityBuilder<H>>
|
||||||
UserCredentialRepository userCredentials = getSharedOrBean(http, UserCredentialRepository.class)
|
UserCredentialRepository userCredentials = getSharedOrBean(http, UserCredentialRepository.class)
|
||||||
.orElse(userCredentialRepository());
|
.orElse(userCredentialRepository());
|
||||||
WebAuthnRelyingPartyOperations rpOperations = webAuthnRelyingPartyOperations(userEntities, userCredentials);
|
WebAuthnRelyingPartyOperations rpOperations = webAuthnRelyingPartyOperations(userEntities, userCredentials);
|
||||||
|
PublicKeyCredentialCreationOptionsRepository creationOptionsRepository = creationOptionsRepository();
|
||||||
WebAuthnAuthenticationFilter webAuthnAuthnFilter = new WebAuthnAuthenticationFilter();
|
WebAuthnAuthenticationFilter webAuthnAuthnFilter = new WebAuthnAuthenticationFilter();
|
||||||
webAuthnAuthnFilter.setAuthenticationManager(
|
webAuthnAuthnFilter.setAuthenticationManager(
|
||||||
new ProviderManager(new WebAuthnAuthenticationProvider(rpOperations, userDetailsService)));
|
new ProviderManager(new WebAuthnAuthenticationProvider(rpOperations, userDetailsService)));
|
||||||
|
@ -148,6 +163,10 @@ public class WebAuthnConfigurer<H extends HttpSecurityBuilder<H>>
|
||||||
rpOperations);
|
rpOperations);
|
||||||
PublicKeyCredentialCreationOptionsFilter creationOptionsFilter = new PublicKeyCredentialCreationOptionsFilter(
|
PublicKeyCredentialCreationOptionsFilter creationOptionsFilter = new PublicKeyCredentialCreationOptionsFilter(
|
||||||
rpOperations);
|
rpOperations);
|
||||||
|
if (creationOptionsRepository != null) {
|
||||||
|
webAuthnRegistrationFilter.setCreationOptionsRepository(creationOptionsRepository);
|
||||||
|
creationOptionsFilter.setCreationOptionsRepository(creationOptionsRepository);
|
||||||
|
}
|
||||||
if (this.converter != null) {
|
if (this.converter != null) {
|
||||||
webAuthnRegistrationFilter.setConverter(this.converter);
|
webAuthnRegistrationFilter.setConverter(this.converter);
|
||||||
creationOptionsFilter.setConverter(this.converter);
|
creationOptionsFilter.setConverter(this.converter);
|
||||||
|
@ -181,6 +200,14 @@ public class WebAuthnConfigurer<H extends HttpSecurityBuilder<H>>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private PublicKeyCredentialCreationOptionsRepository creationOptionsRepository() {
|
||||||
|
if (this.creationOptionsRepository != null) {
|
||||||
|
return this.creationOptionsRepository;
|
||||||
|
}
|
||||||
|
ApplicationContext context = getBuilder().getSharedObject(ApplicationContext.class);
|
||||||
|
return context.getBeanProvider(PublicKeyCredentialCreationOptionsRepository.class).getIfUnique();
|
||||||
|
}
|
||||||
|
|
||||||
private <C> Optional<C> getSharedOrBean(H http, Class<C> type) {
|
private <C> Optional<C> getSharedOrBean(H http, Class<C> type) {
|
||||||
C shared = http.getSharedObject(type);
|
C shared = http.getSharedObject(type);
|
||||||
return Optional.ofNullable(shared).or(() -> getBeanOrNull(type));
|
return Optional.ofNullable(shared).or(() -> getBeanOrNull(type));
|
||||||
|
|
|
@ -18,6 +18,7 @@ package org.springframework.security.config.annotation.web
|
||||||
|
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||||
import org.springframework.security.config.annotation.web.configurers.WebAuthnConfigurer
|
import org.springframework.security.config.annotation.web.configurers.WebAuthnConfigurer
|
||||||
|
import org.springframework.security.web.webauthn.registration.PublicKeyCredentialCreationOptionsRepository
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Kotlin DSL to configure [HttpSecurity] webauthn using idiomatic Kotlin code.
|
* A Kotlin DSL to configure [HttpSecurity] webauthn using idiomatic Kotlin code.
|
||||||
|
@ -35,6 +36,7 @@ class WebAuthnDsl {
|
||||||
var rpId: String? = null
|
var rpId: String? = null
|
||||||
var allowedOrigins: Set<String>? = null
|
var allowedOrigins: Set<String>? = null
|
||||||
var disableDefaultRegistrationPage: Boolean? = false
|
var disableDefaultRegistrationPage: Boolean? = false
|
||||||
|
var creationOptionsRepository: PublicKeyCredentialCreationOptionsRepository? = null
|
||||||
|
|
||||||
internal fun get(): (WebAuthnConfigurer<HttpSecurity>) -> Unit {
|
internal fun get(): (WebAuthnConfigurer<HttpSecurity>) -> Unit {
|
||||||
return { webAuthn ->
|
return { webAuthn ->
|
||||||
|
@ -42,6 +44,7 @@ class WebAuthnDsl {
|
||||||
rpId?.also { webAuthn.rpId(rpId) }
|
rpId?.also { webAuthn.rpId(rpId) }
|
||||||
allowedOrigins?.also { webAuthn.allowedOrigins(allowedOrigins) }
|
allowedOrigins?.also { webAuthn.allowedOrigins(allowedOrigins) }
|
||||||
disableDefaultRegistrationPage?.also { webAuthn.disableDefaultRegistrationPage(disableDefaultRegistrationPage!!) }
|
disableDefaultRegistrationPage?.also { webAuthn.disableDefaultRegistrationPage(disableDefaultRegistrationPage!!) }
|
||||||
|
creationOptionsRepository?.also { webAuthn.creationOptionsRepository(creationOptionsRepository) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,7 @@ import org.springframework.security.web.authentication.ui.DefaultResourcesFilter
|
||||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;
|
import org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;
|
||||||
import org.springframework.security.web.webauthn.api.TestPublicKeyCredentialCreationOptions;
|
import org.springframework.security.web.webauthn.api.TestPublicKeyCredentialCreationOptions;
|
||||||
import org.springframework.security.web.webauthn.management.WebAuthnRelyingPartyOperations;
|
import org.springframework.security.web.webauthn.management.WebAuthnRelyingPartyOperations;
|
||||||
|
import org.springframework.security.web.webauthn.registration.HttpSessionPublicKeyCredentialCreationOptionsRepository;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
@ -55,6 +56,7 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.request;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -140,6 +142,46 @@ public class WebAuthnConfigurerTests {
|
||||||
this.mvc.perform(get("/login/webauthn.js")).andExpect(status().isNotFound());
|
this.mvc.perform(get("/login/webauthn.js")).andExpect(status().isNotFound());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void webauthnWhenConfiguredPublicKeyCredentialCreationOptionsRepository() throws Exception {
|
||||||
|
TestingAuthenticationToken user = new TestingAuthenticationToken("user", "password", "ROLE_USER");
|
||||||
|
SecurityContextHolder.setContext(new SecurityContextImpl(user));
|
||||||
|
PublicKeyCredentialCreationOptions options = TestPublicKeyCredentialCreationOptions
|
||||||
|
.createPublicKeyCredentialCreationOptions()
|
||||||
|
.build();
|
||||||
|
WebAuthnRelyingPartyOperations rpOperations = mock(WebAuthnRelyingPartyOperations.class);
|
||||||
|
ConfigCredentialCreationOptionsRepository.rpOperations = rpOperations;
|
||||||
|
given(rpOperations.createPublicKeyCredentialCreationOptions(any())).willReturn(options);
|
||||||
|
String attrName = "attrName";
|
||||||
|
HttpSessionPublicKeyCredentialCreationOptionsRepository creationOptionsRepository = new HttpSessionPublicKeyCredentialCreationOptionsRepository();
|
||||||
|
creationOptionsRepository.setAttrName(attrName);
|
||||||
|
ConfigCredentialCreationOptionsRepository.creationOptionsRepository = creationOptionsRepository;
|
||||||
|
this.spring.register(ConfigCredentialCreationOptionsRepository.class).autowire();
|
||||||
|
this.mvc.perform(post("/webauthn/register/options"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(request().sessionAttribute(attrName, options));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void webauthnWhenConfiguredPublicKeyCredentialCreationOptionsRepositoryBeanPresent() throws Exception {
|
||||||
|
TestingAuthenticationToken user = new TestingAuthenticationToken("user", "password", "ROLE_USER");
|
||||||
|
SecurityContextHolder.setContext(new SecurityContextImpl(user));
|
||||||
|
PublicKeyCredentialCreationOptions options = TestPublicKeyCredentialCreationOptions
|
||||||
|
.createPublicKeyCredentialCreationOptions()
|
||||||
|
.build();
|
||||||
|
WebAuthnRelyingPartyOperations rpOperations = mock(WebAuthnRelyingPartyOperations.class);
|
||||||
|
ConfigCredentialCreationOptionsRepositoryFromBean.rpOperations = rpOperations;
|
||||||
|
given(rpOperations.createPublicKeyCredentialCreationOptions(any())).willReturn(options);
|
||||||
|
String attrName = "attrName";
|
||||||
|
HttpSessionPublicKeyCredentialCreationOptionsRepository creationOptionsRepository = new HttpSessionPublicKeyCredentialCreationOptionsRepository();
|
||||||
|
creationOptionsRepository.setAttrName(attrName);
|
||||||
|
ConfigCredentialCreationOptionsRepositoryFromBean.creationOptionsRepository = creationOptionsRepository;
|
||||||
|
this.spring.register(ConfigCredentialCreationOptionsRepositoryFromBean.class).autowire();
|
||||||
|
this.mvc.perform(post("/webauthn/register/options"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(request().sessionAttribute(attrName, options));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void webauthnWhenConfiguredMessageConverter() throws Exception {
|
public void webauthnWhenConfiguredMessageConverter() throws Exception {
|
||||||
TestingAuthenticationToken user = new TestingAuthenticationToken("user", "password", "ROLE_USER");
|
TestingAuthenticationToken user = new TestingAuthenticationToken("user", "password", "ROLE_USER");
|
||||||
|
@ -165,6 +207,63 @@ public class WebAuthnConfigurerTests {
|
||||||
.andExpect(content().string(expectedBody));
|
.andExpect(content().string(expectedBody));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
static class ConfigCredentialCreationOptionsRepository {
|
||||||
|
|
||||||
|
private static HttpSessionPublicKeyCredentialCreationOptionsRepository creationOptionsRepository;
|
||||||
|
|
||||||
|
private static WebAuthnRelyingPartyOperations rpOperations;
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
WebAuthnRelyingPartyOperations webAuthnRelyingPartyOperations() {
|
||||||
|
return ConfigCredentialCreationOptionsRepository.rpOperations;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
UserDetailsService userDetailsService() {
|
||||||
|
return new InMemoryUserDetailsManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||||
|
return http.csrf(AbstractHttpConfigurer::disable)
|
||||||
|
.webAuthn((c) -> c.creationOptionsRepository(creationOptionsRepository))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
static class ConfigCredentialCreationOptionsRepositoryFromBean {
|
||||||
|
|
||||||
|
private static HttpSessionPublicKeyCredentialCreationOptionsRepository creationOptionsRepository;
|
||||||
|
|
||||||
|
private static WebAuthnRelyingPartyOperations rpOperations;
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
WebAuthnRelyingPartyOperations webAuthnRelyingPartyOperations() {
|
||||||
|
return ConfigCredentialCreationOptionsRepositoryFromBean.rpOperations;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
UserDetailsService userDetailsService() {
|
||||||
|
return new InMemoryUserDetailsManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
HttpSessionPublicKeyCredentialCreationOptionsRepository creationOptionsRepository() {
|
||||||
|
return ConfigCredentialCreationOptionsRepositoryFromBean.creationOptionsRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||||
|
return http.csrf(AbstractHttpConfigurer::disable).webAuthn(Customizer.withDefaults()).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableWebSecurity
|
@EnableWebSecurity
|
||||||
static class ConfigMessageConverter {
|
static class ConfigMessageConverter {
|
||||||
|
|
|
@ -30,6 +30,7 @@ import org.springframework.security.core.userdetails.User
|
||||||
import org.springframework.security.core.userdetails.UserDetailsService
|
import org.springframework.security.core.userdetails.UserDetailsService
|
||||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager
|
import org.springframework.security.provisioning.InMemoryUserDetailsManager
|
||||||
import org.springframework.security.web.SecurityFilterChain
|
import org.springframework.security.web.SecurityFilterChain
|
||||||
|
import org.springframework.security.web.webauthn.registration.HttpSessionPublicKeyCredentialCreationOptionsRepository
|
||||||
import org.springframework.test.web.servlet.MockMvc
|
import org.springframework.test.web.servlet.MockMvc
|
||||||
import org.springframework.test.web.servlet.get
|
import org.springframework.test.web.servlet.get
|
||||||
import org.springframework.test.web.servlet.post
|
import org.springframework.test.web.servlet.post
|
||||||
|
@ -58,6 +59,16 @@ class WebAuthnDslTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `explicit PublicKeyCredentialCreationOptionsRepository`() {
|
||||||
|
this.spring.register(ExplicitPublicKeyCredentialCreationOptionsRepositoryConfig::class.java).autowire()
|
||||||
|
|
||||||
|
this.mockMvc.post("/test1")
|
||||||
|
.andExpect {
|
||||||
|
status { isForbidden() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `webauthn and formLogin configured with default registration page`() {
|
fun `webauthn and formLogin configured with default registration page`() {
|
||||||
spring.register(DefaultWebauthnConfig::class.java).autowire()
|
spring.register(DefaultWebauthnConfig::class.java).autowire()
|
||||||
|
@ -128,6 +139,33 @@ class WebAuthnDslTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
open class ExplicitPublicKeyCredentialCreationOptionsRepositoryConfig {
|
||||||
|
@Bean
|
||||||
|
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
|
||||||
|
http {
|
||||||
|
webAuthn {
|
||||||
|
rpName = "Spring Security Relying Party"
|
||||||
|
rpId = "example.com"
|
||||||
|
allowedOrigins = setOf("https://example.com")
|
||||||
|
creationOptionsRepository = HttpSessionPublicKeyCredentialCreationOptionsRepository()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return http.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
open fun userDetailsService(): UserDetailsService {
|
||||||
|
val userDetails = User.withDefaultPasswordEncoder()
|
||||||
|
.username("rod")
|
||||||
|
.password("password")
|
||||||
|
.roles("USER")
|
||||||
|
.build()
|
||||||
|
return InMemoryUserDetailsManager(userDetails)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableWebSecurity
|
@EnableWebSecurity
|
||||||
open class WebauthnConfig {
|
open class WebauthnConfig {
|
||||||
|
|
|
@ -60,6 +60,7 @@ Java::
|
||||||
----
|
----
|
||||||
@Bean
|
@Bean
|
||||||
SecurityFilterChain filterChain(HttpSecurity http) {
|
SecurityFilterChain filterChain(HttpSecurity http) {
|
||||||
|
// ...
|
||||||
http
|
http
|
||||||
// ...
|
// ...
|
||||||
.formLogin(withDefaults())
|
.formLogin(withDefaults())
|
||||||
|
@ -67,6 +68,8 @@ SecurityFilterChain filterChain(HttpSecurity http) {
|
||||||
.rpName("Spring Security Relying Party")
|
.rpName("Spring Security Relying Party")
|
||||||
.rpId("example.com")
|
.rpId("example.com")
|
||||||
.allowedOrigins("https://example.com")
|
.allowedOrigins("https://example.com")
|
||||||
|
// optional properties
|
||||||
|
.creationOptionsRepository(new CustomPublicKeyCredentialCreationOptionsRepository())
|
||||||
);
|
);
|
||||||
return http.build();
|
return http.build();
|
||||||
}
|
}
|
||||||
|
@ -89,11 +92,14 @@ Kotlin::
|
||||||
----
|
----
|
||||||
@Bean
|
@Bean
|
||||||
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
|
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
|
||||||
|
// ...
|
||||||
http {
|
http {
|
||||||
webAuthn {
|
webAuthn {
|
||||||
rpName = "Spring Security Relying Party"
|
rpName = "Spring Security Relying Party"
|
||||||
rpId = "example.com"
|
rpId = "example.com"
|
||||||
allowedOrigins = setOf("https://example.com")
|
allowedOrigins = setOf("https://example.com")
|
||||||
|
// optional properties
|
||||||
|
creationOptionsRepository = CustomPublicKeyCredentialCreationOptionsRepository()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -110,6 +116,36 @@ open fun userDetailsService(): UserDetailsService {
|
||||||
----
|
----
|
||||||
======
|
======
|
||||||
|
|
||||||
|
[[passkeys-configuration-pkccor]]
|
||||||
|
=== Custom PublicKeyCredentialCreationOptionsRepository
|
||||||
|
|
||||||
|
The `PublicKeyCredentialCreationOptionsRepository` is used to persist the `PublicKeyCredentialCreationOptions` between requests.
|
||||||
|
The default is to persist it the `HttpSession`, but at times users may need to customize this behavior.
|
||||||
|
This can be done by setting the optional property `creationOptionsRepository` demonstrated in xref:./passkeys.adoc#passkeys-configuration[Configuration] or by exposing a `PublicKeyCredentialCreationOptionsRepository` Bean:
|
||||||
|
|
||||||
|
[tabs]
|
||||||
|
======
|
||||||
|
Java::
|
||||||
|
+
|
||||||
|
[source,java,role="primary"]
|
||||||
|
----
|
||||||
|
@Bean
|
||||||
|
CustomPublicKeyCredentialCreationOptionsRepository creationOptionsRepository() {
|
||||||
|
return new CustomPublicKeyCredentialCreationOptionsRepository();
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
Kotlin::
|
||||||
|
+
|
||||||
|
[source,kotlin,role="secondary"]
|
||||||
|
----
|
||||||
|
@Bean
|
||||||
|
open fun creationOptionsRepository(): CustomPublicKeyCredentialCreationOptionsRepository {
|
||||||
|
return CustomPublicKeyCredentialCreationOptionsRepository()
|
||||||
|
}
|
||||||
|
----
|
||||||
|
======
|
||||||
|
|
||||||
[[passkeys-register]]
|
[[passkeys-register]]
|
||||||
== Register a New Credential
|
== Register a New Credential
|
||||||
|
|
||||||
|
|
|
@ -14,3 +14,7 @@ Note that this may affect reports that operate on this key name.
|
||||||
== OAuth
|
== OAuth
|
||||||
|
|
||||||
* https://github.com/spring-projects/spring-security/pull/16386[gh-16386] - Enable PKCE for confidential clients using `ClientRegistration.clientSettings.requireProofKey=true` for xref:servlet/oauth2/client/core.adoc#oauth2Client-client-registration-requireProofKey[servlet] and xref:reactive/oauth2/client/core.adoc#oauth2Client-client-registration-requireProofKey[reactive] applications
|
* https://github.com/spring-projects/spring-security/pull/16386[gh-16386] - Enable PKCE for confidential clients using `ClientRegistration.clientSettings.requireProofKey=true` for xref:servlet/oauth2/client/core.adoc#oauth2Client-client-registration-requireProofKey[servlet] and xref:reactive/oauth2/client/core.adoc#oauth2Client-client-registration-requireProofKey[reactive] applications
|
||||||
|
|
||||||
|
== WebAuthn
|
||||||
|
|
||||||
|
* https://github.com/spring-projects/spring-security/pull/16396[gh-16396] - Added the ability to configure a custom xref:servlet/authentication/passkeys.adoc#passkeys-configuration-pkccor[`PublicKeyCredentialCreationOptionsRepository`]
|
||||||
|
|
|
@ -105,6 +105,17 @@ public class PublicKeyCredentialCreationOptionsFilter extends OncePerRequestFilt
|
||||||
this.converter.write(options, MediaType.APPLICATION_JSON, new ServletServerHttpResponse(response));
|
this.converter.write(options, MediaType.APPLICATION_JSON, new ServletServerHttpResponse(response));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the {@link PublicKeyCredentialCreationOptionsRepository} to use. The default
|
||||||
|
* is {@link HttpSessionPublicKeyCredentialCreationOptionsRepository}.
|
||||||
|
* @param creationOptionsRepository the
|
||||||
|
* {@link PublicKeyCredentialCreationOptionsRepository} to use. Cannot be null.
|
||||||
|
*/
|
||||||
|
public void setCreationOptionsRepository(PublicKeyCredentialCreationOptionsRepository creationOptionsRepository) {
|
||||||
|
Assert.notNull(creationOptionsRepository, "creationOptionsRepository cannot be null");
|
||||||
|
this.repository = creationOptionsRepository;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the {@link HttpMessageConverter} to read the
|
* Set the {@link HttpMessageConverter} to read the
|
||||||
* {@link WebAuthnRegistrationFilter.WebAuthnRegistrationRequest} and write the
|
* {@link WebAuthnRegistrationFilter.WebAuthnRegistrationRequest} and write the
|
||||||
|
|
Loading…
Reference in New Issue