Skip to content

Commit 2f271eb

Browse files
committed
Apply ObjectPostProcessor to the filter in WebAuthnConfigurer
Closes gh-16369 Signed-off-by: DingHao <[email protected]>
1 parent 0e3cfd1 commit 2f271eb

File tree

3 files changed

+92
-5
lines changed

3 files changed

+92
-5
lines changed

config/src/main/java/org/springframework/security/config/annotation/web/configurers/WebAuthnConfigurer.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2024 the original author or authors.
2+
* Copyright 2002-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -50,6 +50,7 @@
5050
*
5151
* @param <H> the type of builder
5252
* @author Rob Winch
53+
* @author DingHao
5354
* @since 6.4
5455
*/
5556
public class WebAuthnConfigurer<H extends HttpSecurityBuilder<H>>
@@ -130,10 +131,14 @@ public void configure(H http) throws Exception {
130131
WebAuthnAuthenticationFilter webAuthnAuthnFilter = new WebAuthnAuthenticationFilter();
131132
webAuthnAuthnFilter.setAuthenticationManager(
132133
new ProviderManager(new WebAuthnAuthenticationProvider(rpOperations, userDetailsService)));
134+
webAuthnAuthnFilter = postProcess(webAuthnAuthnFilter);
133135
http.addFilterBefore(webAuthnAuthnFilter, BasicAuthenticationFilter.class);
134-
http.addFilterAfter(new WebAuthnRegistrationFilter(userCredentials, rpOperations), AuthorizationFilter.class);
135-
http.addFilterBefore(new PublicKeyCredentialCreationOptionsFilter(rpOperations), AuthorizationFilter.class);
136-
http.addFilterBefore(new PublicKeyCredentialRequestOptionsFilter(rpOperations), AuthorizationFilter.class);
136+
http.addFilterAfter(postProcess(new WebAuthnRegistrationFilter(userCredentials, rpOperations)),
137+
AuthorizationFilter.class);
138+
http.addFilterBefore(postProcess(new PublicKeyCredentialCreationOptionsFilter(rpOperations)),
139+
AuthorizationFilter.class);
140+
http.addFilterBefore(postProcess(new PublicKeyCredentialRequestOptionsFilter(rpOperations)),
141+
AuthorizationFilter.class);
137142

138143
DefaultLoginPageGeneratingFilter loginPageGeneratingFilter = http
139144
.getSharedObject(DefaultLoginPageGeneratingFilter.class);

config/src/test/java/org/springframework/security/config/annotation/web/configurers/WebAuthnConfigurerTests.java

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2024 the original author or authors.
2+
* Copyright 2002-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -21,7 +21,9 @@
2121
import org.junit.jupiter.api.Test;
2222
import org.junit.jupiter.api.extension.ExtendWith;
2323

24+
import org.springframework.beans.BeansException;
2425
import org.springframework.beans.factory.annotation.Autowired;
26+
import org.springframework.beans.factory.config.BeanPostProcessor;
2527
import org.springframework.context.annotation.Bean;
2628
import org.springframework.context.annotation.Configuration;
2729
import org.springframework.security.config.Customizer;
@@ -34,6 +36,14 @@
3436
import org.springframework.security.web.FilterChainProxy;
3537
import org.springframework.security.web.SecurityFilterChain;
3638
import org.springframework.security.web.authentication.ui.DefaultResourcesFilter;
39+
import org.springframework.security.web.webauthn.authentication.HttpSessionPublicKeyCredentialRequestOptionsRepository;
40+
import org.springframework.security.web.webauthn.authentication.PublicKeyCredentialRequestOptionsFilter;
41+
import org.springframework.security.web.webauthn.authentication.PublicKeyCredentialRequestOptionsRepository;
42+
import org.springframework.security.web.webauthn.authentication.WebAuthnAuthenticationFilter;
43+
import org.springframework.security.web.webauthn.registration.HttpSessionPublicKeyCredentialCreationOptionsRepository;
44+
import org.springframework.security.web.webauthn.registration.PublicKeyCredentialCreationOptionsFilter;
45+
import org.springframework.security.web.webauthn.registration.PublicKeyCredentialCreationOptionsRepository;
46+
import org.springframework.security.web.webauthn.registration.WebAuthnRegistrationFilter;
3747
import org.springframework.test.web.servlet.MockMvc;
3848

3949
import static org.assertj.core.api.Assertions.assertThat;
@@ -126,6 +136,67 @@ public void webauthnWhenConfiguredAndNoDefaultRegistrationPageThenDoesNotServeJa
126136
this.mvc.perform(get("/login/webauthn.js")).andExpect(status().isNotFound());
127137
}
128138

139+
@Test
140+
public void configWebauthn() {
141+
this.spring.register(WebauthnFilterConfiguration.class).autowire();
142+
assertThat(WebauthnFilterConfiguration.count).isEqualTo(4);
143+
}
144+
145+
@Configuration
146+
@EnableWebSecurity
147+
static class WebauthnFilterConfiguration {
148+
149+
static int count = 0;
150+
151+
@Bean
152+
PublicKeyCredentialRequestOptionsRepository requestOptionsRepository() {
153+
return new HttpSessionPublicKeyCredentialRequestOptionsRepository();
154+
}
155+
156+
@Bean
157+
PublicKeyCredentialCreationOptionsRepository creationOptionsRepository() {
158+
return new HttpSessionPublicKeyCredentialCreationOptionsRepository();
159+
}
160+
161+
@Bean
162+
static BeanPostProcessor beanPostProcessor(PublicKeyCredentialRequestOptionsRepository requestOptionsRepository,
163+
PublicKeyCredentialCreationOptionsRepository creationOptionsRepository) {
164+
return new BeanPostProcessor() {
165+
@Override
166+
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
167+
if (bean instanceof WebAuthnAuthenticationFilter filter) {
168+
filter.setRequestOptionsRepository(requestOptionsRepository);
169+
count++;
170+
}
171+
else if (bean instanceof WebAuthnRegistrationFilter filter) {
172+
filter.setCreationOptionsRepository(creationOptionsRepository);
173+
count++;
174+
}
175+
else if (bean instanceof PublicKeyCredentialCreationOptionsFilter filter) {
176+
filter.setCreationOptionsRepository(creationOptionsRepository);
177+
count++;
178+
}
179+
else if (bean instanceof PublicKeyCredentialRequestOptionsFilter filter) {
180+
filter.setRequestOptionsRepository(requestOptionsRepository);
181+
count++;
182+
}
183+
return bean;
184+
}
185+
};
186+
}
187+
188+
@Bean
189+
UserDetailsService userDetailsService() {
190+
return new InMemoryUserDetailsManager();
191+
}
192+
193+
@Bean
194+
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
195+
return http.webAuthn(Customizer.withDefaults()).build();
196+
}
197+
198+
}
199+
129200
@Configuration
130201
@EnableWebSecurity
131202
static class DefaultWebauthnConfiguration {

web/src/main/java/org/springframework/security/web/webauthn/registration/PublicKeyCredentialCreationOptionsFilter.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,4 +103,15 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
103103
this.converter.write(options, MediaType.APPLICATION_JSON, new ServletServerHttpResponse(response));
104104
}
105105

106+
/**
107+
* Sets the {@link PublicKeyCredentialCreationOptionsRepository} to use. The default
108+
* is {@link HttpSessionPublicKeyCredentialCreationOptionsRepository}.
109+
* @param creationOptionsRepository the
110+
* {@link PublicKeyCredentialCreationOptionsRepository} to use. Cannot be null.
111+
*/
112+
public void setCreationOptionsRepository(PublicKeyCredentialCreationOptionsRepository creationOptionsRepository) {
113+
Assert.notNull(creationOptionsRepository, "creationOptionsRepository cannot be null");
114+
this.repository = creationOptionsRepository;
115+
}
116+
106117
}

0 commit comments

Comments
 (0)