Skip to content

Apply ObjectPostProcessor to the filter in WebAuthnConfigurer #16371

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 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.
Expand Down Expand Up @@ -50,6 +50,7 @@
*
* @param <H> the type of builder
* @author Rob Winch
* @author DingHao
* @since 6.4
*/
public class WebAuthnConfigurer<H extends HttpSecurityBuilder<H>>
Expand Down Expand Up @@ -130,10 +131,14 @@ public void configure(H http) throws Exception {
WebAuthnAuthenticationFilter webAuthnAuthnFilter = new WebAuthnAuthenticationFilter();
webAuthnAuthnFilter.setAuthenticationManager(
new ProviderManager(new WebAuthnAuthenticationProvider(rpOperations, userDetailsService)));
webAuthnAuthnFilter = postProcess(webAuthnAuthnFilter);
http.addFilterBefore(webAuthnAuthnFilter, BasicAuthenticationFilter.class);
http.addFilterAfter(new WebAuthnRegistrationFilter(userCredentials, rpOperations), AuthorizationFilter.class);
http.addFilterBefore(new PublicKeyCredentialCreationOptionsFilter(rpOperations), AuthorizationFilter.class);
http.addFilterBefore(new PublicKeyCredentialRequestOptionsFilter(rpOperations), AuthorizationFilter.class);
http.addFilterAfter(postProcess(new WebAuthnRegistrationFilter(userCredentials, rpOperations)),
AuthorizationFilter.class);
http.addFilterBefore(postProcess(new PublicKeyCredentialCreationOptionsFilter(rpOperations)),
AuthorizationFilter.class);
http.addFilterBefore(postProcess(new PublicKeyCredentialRequestOptionsFilter(rpOperations)),
AuthorizationFilter.class);

DefaultLoginPageGeneratingFilter loginPageGeneratingFilter = http
.getSharedObject(DefaultLoginPageGeneratingFilter.class);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 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.
Expand All @@ -21,7 +21,9 @@
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
Expand All @@ -34,6 +36,14 @@
import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.ui.DefaultResourcesFilter;
import org.springframework.security.web.webauthn.authentication.HttpSessionPublicKeyCredentialRequestOptionsRepository;
import org.springframework.security.web.webauthn.authentication.PublicKeyCredentialRequestOptionsFilter;
import org.springframework.security.web.webauthn.authentication.PublicKeyCredentialRequestOptionsRepository;
import org.springframework.security.web.webauthn.authentication.WebAuthnAuthenticationFilter;
import org.springframework.security.web.webauthn.registration.HttpSessionPublicKeyCredentialCreationOptionsRepository;
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.test.web.servlet.MockMvc;

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

@Test
public void configWebauthn() {
this.spring.register(WebauthnFilterConfiguration.class).autowire();
assertThat(WebauthnFilterConfiguration.count).isEqualTo(4);
}

@Configuration
@EnableWebSecurity
static class WebauthnFilterConfiguration {

static int count = 0;

@Bean
PublicKeyCredentialRequestOptionsRepository requestOptionsRepository() {
return new HttpSessionPublicKeyCredentialRequestOptionsRepository();
}

@Bean
PublicKeyCredentialCreationOptionsRepository creationOptionsRepository() {
return new HttpSessionPublicKeyCredentialCreationOptionsRepository();
}

@Bean
static BeanPostProcessor beanPostProcessor(PublicKeyCredentialRequestOptionsRepository requestOptionsRepository,
PublicKeyCredentialCreationOptionsRepository creationOptionsRepository) {
return new BeanPostProcessor() {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof WebAuthnAuthenticationFilter filter) {
filter.setRequestOptionsRepository(requestOptionsRepository);
count++;
}
else if (bean instanceof WebAuthnRegistrationFilter filter) {
filter.setCreationOptionsRepository(creationOptionsRepository);
count++;
}
else if (bean instanceof PublicKeyCredentialCreationOptionsFilter filter) {
filter.setCreationOptionsRepository(creationOptionsRepository);
count++;
}
else if (bean instanceof PublicKeyCredentialRequestOptionsFilter filter) {
filter.setRequestOptionsRepository(requestOptionsRepository);
count++;
}
return bean;
}
};
}

@Bean
UserDetailsService userDetailsService() {
return new InMemoryUserDetailsManager();
}

@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http.webAuthn(Customizer.withDefaults()).build();
}

}

@Configuration
@EnableWebSecurity
static class DefaultWebauthnConfiguration {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,4 +103,15 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
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;
}

}
Loading