瀏覽代碼

Limit oauth2Login() links to redirect-based flows

This prevents the generated login page from showing links for
authorization grant types like "client_credentials" which are
not redirect-based, and thus not meant for interactive use in
the browser.

Closes gh-9457
Denis Washington 4 年之前
父節點
當前提交
2b4b856b32

+ 8 - 3
config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurer.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2020 the original author or authors.
+ * Copyright 2002-2021 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.
@@ -58,6 +58,7 @@ import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequest
 import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver;
 import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
 import org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter;
+import org.springframework.security.oauth2.core.AuthorizationGrantType;
 import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
 import org.springframework.security.oauth2.core.OAuth2Error;
 import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
@@ -485,8 +486,12 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
 				? this.authorizationEndpointConfig.authorizationRequestBaseUri
 				: OAuth2AuthorizationRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI;
 		Map<String, String> loginUrlToClientName = new HashMap<>();
-		clientRegistrations.forEach((registration) -> loginUrlToClientName.put(
-				authorizationRequestBaseUri + "/" + registration.getRegistrationId(), registration.getClientName()));
+		clientRegistrations.forEach((registration) -> {
+			if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(registration.getAuthorizationGrantType())) {
+				String authorizationRequestUri = authorizationRequestBaseUri + "/" + registration.getRegistrationId();
+				loginUrlToClientName.put(authorizationRequestUri, registration.getClientName());
+			}
+		});
 		return loginUrlToClientName;
 	}
 

+ 6 - 2
config/src/main/java/org/springframework/security/config/web/server/ServerHttpSecurity.java

@@ -80,6 +80,7 @@ import org.springframework.security.oauth2.client.web.server.ServerOAuth2Authori
 import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository;
 import org.springframework.security.oauth2.client.web.server.WebSessionOAuth2ServerAuthorizationRequestRepository;
 import org.springframework.security.oauth2.client.web.server.authentication.OAuth2LoginAuthenticationWebFilter;
+import org.springframework.security.oauth2.core.AuthorizationGrantType;
 import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
 import org.springframework.security.oauth2.core.OAuth2AuthorizationException;
 import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
@@ -3356,8 +3357,11 @@ public class ServerHttpSecurity {
 				return Collections.emptyMap();
 			}
 			Map<String, String> result = new HashMap<>();
-			registrations.iterator().forEachRemaining(
-					(r) -> result.put("/oauth2/authorization/" + r.getRegistrationId(), r.getClientName()));
+			registrations.iterator().forEachRemaining((r) -> {
+				if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(r.getAuthorizationGrantType())) {
+					result.put("/oauth2/authorization/" + r.getRegistrationId(), r.getClientName());
+				}
+			});
 			return result;
 		}
 

+ 35 - 1
config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurerTests.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2019 the original author or authors.
+ * Copyright 2002-2021 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.
@@ -126,6 +126,11 @@ public class OAuth2LoginConfigurerTests {
 			.build();
 	// @formatter:on
 
+	// @formatter:off
+	private static final ClientRegistration CLIENT_CREDENTIALS_REGISTRATION = TestClientRegistrations.clientCredentials()
+			.build();
+	// @formatter:on
+
 	private ConfigurableApplicationContext context;
 
 	@Autowired
@@ -396,6 +401,18 @@ public class OAuth2LoginConfigurerTests {
 		assertThat(this.response.getRedirectedUrl()).doesNotMatch("http://localhost/oauth2/authorization/google");
 	}
 
+	// gh-9457
+	@Test
+	public void oauth2LoginWithOneAuthorizationCodeClientAndOtherClientsConfiguredThenRedirectForAuthorization()
+			throws Exception {
+		loadConfig(OAuth2LoginConfigAuthorizationCodeClientAndOtherClients.class);
+		String requestUri = "/";
+		this.request = new MockHttpServletRequest("GET", requestUri);
+		this.request.setServletPath(requestUri);
+		this.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain);
+		assertThat(this.response.getRedirectedUrl()).matches("http://localhost/oauth2/authorization/google");
+	}
+
 	@Test
 	public void oauth2LoginWithCustomLoginPageThenRedirectCustomLoginPage() throws Exception {
 		loadConfig(OAuth2LoginConfigCustomLoginPage.class);
@@ -799,6 +816,23 @@ public class OAuth2LoginConfigurerTests {
 
 	}
 
+	@EnableWebSecurity
+	static class OAuth2LoginConfigAuthorizationCodeClientAndOtherClients extends CommonWebSecurityConfigurerAdapter {
+
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+					.oauth2Login()
+					.clientRegistrationRepository(
+							new InMemoryClientRegistrationRepository(
+									GOOGLE_CLIENT_REGISTRATION, CLIENT_CREDENTIALS_REGISTRATION));
+			// @formatter:on
+			super.configure(http);
+		}
+
+	}
+
 	@EnableWebSecurity
 	static class OAuth2LoginConfigCustomLoginPage extends CommonWebSecurityConfigurerAdapter {
 

+ 31 - 0
config/src/test/java/org/springframework/security/config/web/server/OAuth2LoginTests.java

@@ -134,6 +134,11 @@ public class OAuth2LoginTests {
 	private static ClientRegistration google = CommonOAuth2Provider.GOOGLE.getBuilder("google").clientId("client")
 			.clientSecret("secret").build();
 
+	// @formatter:off
+	private static ClientRegistration clientCredentials = TestClientRegistrations.clientCredentials()
+			.build();
+	// @formatter:on
+
 	@Autowired
 	public void setApplicationContext(ApplicationContext context) {
 		if (context.getBeanNamesForType(WebHandler.class).length > 0) {
@@ -179,6 +184,22 @@ public class OAuth2LoginTests {
 		assertThat(driver.getCurrentUrl()).startsWith("https://github.com/login/oauth/authorize");
 	}
 
+	// gh-9457
+	@Test
+	public void defaultLoginPageWithAuthorizationCodeAndClientCredentialsClientRegistrationThenRedirect() {
+		this.spring.register(OAuth2LoginWithAuthorizationCodeAndClientCredentialsClientRegistration.class).autowire();
+		// @formatter:off
+		WebTestClient webTestClient = WebTestClientBuilder
+				.bindToWebFilters(new GitHubWebFilter(), this.springSecurity)
+				.build();
+		WebDriver driver = WebTestClientHtmlUnitDriverBuilder
+				.webTestClientSetup(webTestClient)
+				.build();
+		// @formatter:on
+		driver.get("http://localhost/");
+		assertThat(driver.getCurrentUrl()).startsWith("https://github.com/login/oauth/authorize");
+	}
+
 	@Test
 	public void defaultLoginPageWithSingleClientRegistrationAndFormLoginThenLinks() {
 		this.spring.register(OAuth2LoginWithSingleClientRegistrations.class, OAuth2LoginWithFormLogin.class).autowire();
@@ -564,6 +585,16 @@ public class OAuth2LoginTests {
 
 	}
 
+	@EnableWebFluxSecurity
+	static class OAuth2LoginWithAuthorizationCodeAndClientCredentialsClientRegistration {
+
+		@Bean
+		InMemoryReactiveClientRegistrationRepository clientRegistrationRepository() {
+			return new InMemoryReactiveClientRegistrationRepository(github, clientCredentials);
+		}
+
+	}
+
 	@EnableWebFlux
 	static class OAuth2AuthorizeWithMockObjectsConfig {