Skip to content

Commit 4706b16

Browse files
committed
oauth2Login WebFlux does not auto-redirect for XHR request
Fixes gh-8118
1 parent 98bd1a3 commit 4706b16

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.
@@ -1181,22 +1181,54 @@ protected void configure(ServerHttpSecurity http) {
11811181
authenticationFilter.setAuthenticationFailureHandler(getAuthenticationFailureHandler());
11821182
authenticationFilter.setSecurityContextRepository(this.securityContextRepository);
11831183

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

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

12021234
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)