Skip to content

Commit de7c452

Browse files
Kehrlannrwinch
authored andcommitted
webauthn: use DefaultResourcesFilter#webauthn
- Unconditionally use the DefaultResourcesFilter, because the javascript file is required by the DefaultWebAythnPageGeneratingFilter, which is always registered.
1 parent a152636 commit de7c452

File tree

2 files changed

+80
-22
lines changed

2 files changed

+80
-22
lines changed

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

Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,13 @@
1616

1717
package org.springframework.security.config.annotation.web.configurers;
1818

19-
import java.lang.reflect.Constructor;
2019
import java.util.HashSet;
2120
import java.util.Map;
2221
import java.util.Optional;
2322
import java.util.Set;
2423

2524
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
2625
import org.springframework.context.ApplicationContext;
27-
import org.springframework.core.io.ClassPathResource;
28-
import org.springframework.http.HttpMethod;
29-
import org.springframework.http.MediaType;
3026
import org.springframework.security.authentication.ProviderManager;
3127
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
3228
import org.springframework.security.core.userdetails.UserDetailsService;
@@ -35,8 +31,6 @@
3531
import org.springframework.security.web.authentication.ui.DefaultResourcesFilter;
3632
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
3733
import org.springframework.security.web.csrf.CsrfToken;
38-
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
39-
import org.springframework.security.web.util.matcher.RequestMatcher;
4034
import org.springframework.security.web.webauthn.api.PublicKeyCredentialRpEntity;
4135
import org.springframework.security.web.webauthn.authentication.PublicKeyCredentialRequestOptionsFilter;
4236
import org.springframework.security.web.webauthn.authentication.WebAuthnAuthenticationFilter;
@@ -51,8 +45,6 @@
5145
import org.springframework.security.web.webauthn.registration.PublicKeyCredentialCreationOptionsFilter;
5246
import org.springframework.security.web.webauthn.registration.WebAuthnRegistrationFilter;
5347

54-
import static org.springframework.security.web.util.matcher.AntPathRequestMatcher.antMatcher;
55-
5648
/**
5749
* Configures WebAuthn for Spring Security applications
5850
*
@@ -133,23 +125,12 @@ public void configure(H http) throws Exception {
133125
DefaultLoginPageGeneratingFilter loginPageGeneratingFilter = http
134126
.getSharedObject(DefaultLoginPageGeneratingFilter.class);
135127
if (loginPageGeneratingFilter != null) {
136-
ClassPathResource webauthn = new ClassPathResource(
137-
"org/springframework/security/spring-security-webauthn.js");
138-
AntPathRequestMatcher matcher = antMatcher(HttpMethod.GET, "/login/webauthn.js");
139-
140-
Constructor<DefaultResourcesFilter> constructor = DefaultResourcesFilter.class
141-
.getDeclaredConstructor(RequestMatcher.class, ClassPathResource.class, MediaType.class);
142-
constructor.setAccessible(true);
143-
DefaultResourcesFilter resourcesFilter = constructor.newInstance(matcher, webauthn,
144-
MediaType.parseMediaType("text/javascript"));
145-
http.addFilter(resourcesFilter);
146-
DefaultLoginPageGeneratingFilter loginGeneratingFilter = http
147-
.getSharedObject(DefaultLoginPageGeneratingFilter.class);
148-
loginGeneratingFilter.setPasskeysEnabled(true);
149-
loginGeneratingFilter.setResolveHeaders((request) -> {
128+
loginPageGeneratingFilter.setPasskeysEnabled(true);
129+
loginPageGeneratingFilter.setResolveHeaders((request) -> {
150130
CsrfToken csrfToken = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
151131
return Map.of(csrfToken.getHeaderName(), csrfToken.getToken());
152132
});
133+
http.addFilter(DefaultResourcesFilter.webauthn());
153134
}
154135
}
155136

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
* Copyright 2002-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.config.annotation.web.configurers;
18+
19+
import org.junit.jupiter.api.Test;
20+
import org.junit.jupiter.api.extension.ExtendWith;
21+
22+
import org.springframework.beans.factory.annotation.Autowired;
23+
import org.springframework.context.annotation.Bean;
24+
import org.springframework.context.annotation.Configuration;
25+
import org.springframework.security.config.Customizer;
26+
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
27+
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
28+
import org.springframework.security.config.test.SpringTestContext;
29+
import org.springframework.security.config.test.SpringTestContextExtension;
30+
import org.springframework.security.core.userdetails.UserDetailsService;
31+
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
32+
import org.springframework.security.web.SecurityFilterChain;
33+
import org.springframework.test.web.servlet.MockMvc;
34+
35+
import static org.hamcrest.Matchers.containsString;
36+
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
37+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
38+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
39+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
40+
41+
/**
42+
* @author Daniel Garnier-Moiroux
43+
*/
44+
@ExtendWith(SpringTestContextExtension.class)
45+
public class WebAuthnConfigurerTests {
46+
47+
public final SpringTestContext spring = new SpringTestContext(this);
48+
49+
@Autowired
50+
MockMvc mvc;
51+
52+
@Test
53+
public void javascriptWhenWebauthnConfiguredThenServesJavascript() throws Exception {
54+
this.spring.register(DefaultWebauthnConfiguration.class).autowire();
55+
this.mvc.perform(get("/login/webauthn.js"))
56+
.andExpect(status().isOk())
57+
.andExpect(header().string("content-type", "text/javascript;charset=UTF-8"))
58+
.andExpect(content().string(containsString("async function authenticate(")));
59+
}
60+
61+
@Configuration
62+
@EnableWebSecurity
63+
static class DefaultWebauthnConfiguration {
64+
65+
@Bean
66+
UserDetailsService userDetailsService() {
67+
return new InMemoryUserDetailsManager();
68+
}
69+
70+
@Bean
71+
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
72+
return http.webAuthn(Customizer.withDefaults()).build();
73+
}
74+
75+
}
76+
77+
}

0 commit comments

Comments
 (0)