Skip to content

Commit 0230827

Browse files
committed
oauth2Login WebFlux does not auto-redirect for XHR request
Fixes gh-8118
1 parent e00d3fc commit 0230827

File tree

2 files changed

+63
-14
lines changed

2 files changed

+63
-14
lines changed

config/src/main/java/org/springframework/security/config/web/server/ServerHttpSecurity.java

+45-13
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2020 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.
@@ -1180,22 +1180,54 @@ protected void configure(ServerHttpSecurity http) {
11801180
authenticationFilter.setAuthenticationFailureHandler(getAuthenticationFailureHandler());
11811181
authenticationFilter.setSecurityContextRepository(this.securityContextRepository);
11821182

1183-
MediaTypeServerWebExchangeMatcher htmlMatcher = new MediaTypeServerWebExchangeMatcher(
1184-
MediaType.TEXT_HTML);
1185-
htmlMatcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));
1183+
setDefaultEntryPoints(http);
1184+
1185+
http.addFilterAt(oauthRedirectFilter, SecurityWebFiltersOrder.HTTP_BASIC);
1186+
http.addFilterAt(authenticationFilter, SecurityWebFiltersOrder.AUTHENTICATION);
1187+
}
1188+
1189+
private void setDefaultEntryPoints(ServerHttpSecurity http) {
1190+
String defaultLoginPage = "/login";
11861191
Map<String, String> urlToText = http.oauth2Login.getLinks();
1187-
String authenticationEntryPointRedirectPath;
1192+
String providerLoginPage = null;
11881193
if (urlToText.size() == 1) {
1189-
authenticationEntryPointRedirectPath = urlToText.keySet().iterator().next();
1190-
} else {
1191-
authenticationEntryPointRedirectPath = "/login";
1194+
providerLoginPage = urlToText.keySet().iterator().next();
11921195
}
1193-
RedirectServerAuthenticationEntryPoint entryPoint = new RedirectServerAuthenticationEntryPoint(authenticationEntryPointRedirectPath);
1194-
entryPoint.setRequestCache(http.requestCache.requestCache);
1195-
http.defaultEntryPoints.add(new DelegateEntry(htmlMatcher, entryPoint));
11961196

1197-
http.addFilterAt(oauthRedirectFilter, SecurityWebFiltersOrder.HTTP_BASIC);
1198-
http.addFilterAt(authenticationFilter, SecurityWebFiltersOrder.AUTHENTICATION);
1197+
MediaTypeServerWebExchangeMatcher htmlMatcher = new MediaTypeServerWebExchangeMatcher(
1198+
MediaType.APPLICATION_XHTML_XML, new MediaType("image", "*"),
1199+
MediaType.TEXT_HTML, MediaType.TEXT_PLAIN);
1200+
htmlMatcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));
1201+
1202+
ServerWebExchangeMatcher xhrMatcher = exchange -> {
1203+
if (exchange.getRequest().getHeaders().getOrEmpty("X-Requested-With").contains("XMLHttpRequest")) {
1204+
return ServerWebExchangeMatcher.MatchResult.match();
1205+
}
1206+
return ServerWebExchangeMatcher.MatchResult.notMatch();
1207+
};
1208+
ServerWebExchangeMatcher notXhrMatcher = new NegatedServerWebExchangeMatcher(xhrMatcher);
1209+
1210+
ServerWebExchangeMatcher defaultEntryPointMatcher = new AndServerWebExchangeMatcher(
1211+
notXhrMatcher, htmlMatcher);
1212+
1213+
if (providerLoginPage != null) {
1214+
ServerWebExchangeMatcher loginPageMatcher = new PathPatternParserServerWebExchangeMatcher(defaultLoginPage);
1215+
ServerWebExchangeMatcher faviconMatcher = new PathPatternParserServerWebExchangeMatcher("/favicon.ico");
1216+
ServerWebExchangeMatcher defaultLoginPageMatcher = new AndServerWebExchangeMatcher(
1217+
new OrServerWebExchangeMatcher(loginPageMatcher, faviconMatcher), defaultEntryPointMatcher);
1218+
1219+
ServerWebExchangeMatcher matcher = new AndServerWebExchangeMatcher(
1220+
notXhrMatcher, new NegatedServerWebExchangeMatcher(defaultLoginPageMatcher));
1221+
RedirectServerAuthenticationEntryPoint entryPoint =
1222+
new RedirectServerAuthenticationEntryPoint(providerLoginPage);
1223+
entryPoint.setRequestCache(http.requestCache.requestCache);
1224+
http.defaultEntryPoints.add(new DelegateEntry(matcher, entryPoint));
1225+
}
1226+
1227+
RedirectServerAuthenticationEntryPoint defaultEntryPoint =
1228+
new RedirectServerAuthenticationEntryPoint(defaultLoginPage);
1229+
defaultEntryPoint.setRequestCache(http.requestCache.requestCache);
1230+
http.defaultEntryPoints.add(new DelegateEntry(defaultEntryPointMatcher, defaultEntryPoint));
11991231
}
12001232

12011233
private ServerAuthenticationSuccessHandler getAuthenticationSuccessHandler(ServerHttpSecurity http) {

config/src/test/java/org/springframework/security/config/web/server/OAuth2LoginTests.java

+18-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2020 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.
@@ -24,6 +24,7 @@
2424
import org.springframework.context.ApplicationContext;
2525
import org.springframework.context.annotation.Bean;
2626
import org.springframework.context.annotation.Configuration;
27+
import org.springframework.http.HttpHeaders;
2728
import org.springframework.security.authentication.ReactiveAuthenticationManager;
2829
import org.springframework.security.authentication.TestingAuthenticationToken;
2930
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
@@ -185,6 +186,22 @@ public void defaultLoginPageWithSingleClientRegistrationThenRedirect() {
185186
assertThat(driver.getCurrentUrl()).startsWith("https://github.com/login/oauth/authorize");
186187
}
187188

189+
// gh-8118
190+
@Test
191+
public void defaultLoginPageWithSingleClientRegistrationAndXhrRequestThenDoesNotRedirectForAuthorization() {
192+
this.spring.register(OAuth2LoginWithSingleClientRegistrations.class, WebFluxConfig.class).autowire();
193+
194+
this.client.get()
195+
.uri("/")
196+
.header("X-Requested-With", "XMLHttpRequest")
197+
.exchange()
198+
.expectStatus().is3xxRedirection()
199+
.expectHeader().valueEquals(HttpHeaders.LOCATION, "/login");
200+
}
201+
202+
@EnableWebFlux
203+
static class WebFluxConfig { }
204+
188205
@EnableWebFluxSecurity
189206
static class OAuth2LoginWithSingleClientRegistrations {
190207
@Bean

0 commit comments

Comments
 (0)