From edf06a34619a16d001f860e837b7811c2e0c3bb5 Mon Sep 17 00:00:00 2001 From: Joe Grandja Date: Mon, 29 Jun 2020 20:44:26 -0400 Subject: [PATCH] OAuth2AuthorizedClientArgumentResolver uses OAuth2AuthorizedClientManager @Bean Closes gh-8700 --- .../OAuth2ClientConfiguration.java | 70 ++++++++++------ .../OAuth2ClientConfigurationTests.java | 80 ++++++++++++++++++- 2 files changed, 124 insertions(+), 26 deletions(-) diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configuration/OAuth2ClientConfiguration.java b/config/src/main/java/org/springframework/security/config/annotation/web/configuration/OAuth2ClientConfiguration.java index b29212d79e..892fb394f0 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configuration/OAuth2ClientConfiguration.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/configuration/OAuth2ClientConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2020 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. @@ -20,6 +20,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.ImportSelector; import org.springframework.core.type.AnnotationMetadata; +import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager; import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProvider; import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProviderBuilder; import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient; @@ -33,7 +34,6 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import java.util.List; -import java.util.Optional; /** * {@link Configuration} for OAuth 2.0 Client support. @@ -67,47 +67,69 @@ final class OAuth2ClientConfiguration { private ClientRegistrationRepository clientRegistrationRepository; private OAuth2AuthorizedClientRepository authorizedClientRepository; private OAuth2AccessTokenResponseClient accessTokenResponseClient; + private OAuth2AuthorizedClientManager authorizedClientManager; @Override public void addArgumentResolvers(List argumentResolvers) { - if (this.clientRegistrationRepository != null && this.authorizedClientRepository != null) { - OAuth2AuthorizedClientProviderBuilder authorizedClientProviderBuilder = - OAuth2AuthorizedClientProviderBuilder.builder() - .authorizationCode() - .refreshToken() - .password(); - if (this.accessTokenResponseClient != null) { - authorizedClientProviderBuilder.clientCredentials(configurer -> - configurer.accessTokenResponseClient(this.accessTokenResponseClient)); - } else { - authorizedClientProviderBuilder.clientCredentials(); - } - OAuth2AuthorizedClientProvider authorizedClientProvider = authorizedClientProviderBuilder.build(); - DefaultOAuth2AuthorizedClientManager authorizedClientManager = new DefaultOAuth2AuthorizedClientManager( - this.clientRegistrationRepository, this.authorizedClientRepository); - authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider); + OAuth2AuthorizedClientManager authorizedClientManager = getAuthorizedClientManager(); + if (authorizedClientManager != null) { argumentResolvers.add(new OAuth2AuthorizedClientArgumentResolver(authorizedClientManager)); } } @Autowired(required = false) - public void setClientRegistrationRepository(List clientRegistrationRepositories) { + void setClientRegistrationRepository(List clientRegistrationRepositories) { if (clientRegistrationRepositories.size() == 1) { this.clientRegistrationRepository = clientRegistrationRepositories.get(0); } } @Autowired(required = false) - public void setAuthorizedClientRepository(List authorizedClientRepositories) { + void setAuthorizedClientRepository(List authorizedClientRepositories) { if (authorizedClientRepositories.size() == 1) { this.authorizedClientRepository = authorizedClientRepositories.get(0); } } - @Autowired - public void setAccessTokenResponseClient( - Optional> accessTokenResponseClient) { - accessTokenResponseClient.ifPresent(client -> this.accessTokenResponseClient = client); + @Autowired(required = false) + void setAccessTokenResponseClient(OAuth2AccessTokenResponseClient accessTokenResponseClient) { + this.accessTokenResponseClient = accessTokenResponseClient; + } + + @Autowired(required = false) + void setAuthorizedClientManager(List authorizedClientManagers) { + if (authorizedClientManagers.size() == 1) { + this.authorizedClientManager = authorizedClientManagers.get(0); + } + } + + private OAuth2AuthorizedClientManager getAuthorizedClientManager() { + if (this.authorizedClientManager != null) { + return this.authorizedClientManager; + } + + OAuth2AuthorizedClientManager authorizedClientManager = null; + if (this.clientRegistrationRepository != null && this.authorizedClientRepository != null) { + if (this.accessTokenResponseClient != null) { + OAuth2AuthorizedClientProvider authorizedClientProvider = + OAuth2AuthorizedClientProviderBuilder.builder() + .authorizationCode() + .refreshToken() + .clientCredentials(configurer -> + configurer.accessTokenResponseClient(this.accessTokenResponseClient)) + .password() + .build(); + DefaultOAuth2AuthorizedClientManager defaultAuthorizedClientManager = + new DefaultOAuth2AuthorizedClientManager( + this.clientRegistrationRepository, this.authorizedClientRepository); + defaultAuthorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider); + authorizedClientManager = defaultAuthorizedClientManager; + } else { + authorizedClientManager = new DefaultOAuth2AuthorizedClientManager( + this.clientRegistrationRepository, this.authorizedClientRepository); + } + } + return authorizedClientManager; } } } diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configuration/OAuth2ClientConfigurationTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configuration/OAuth2ClientConfigurationTests.java index 8c42b037d7..48eb3fd45d 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configuration/OAuth2ClientConfigurationTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configuration/OAuth2ClientConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2020 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. @@ -25,6 +25,7 @@ import org.springframework.security.authentication.TestingAuthenticationToken; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.test.SpringTestRule; import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; +import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager; import org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient; import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient; import org.springframework.security.oauth2.client.endpoint.OAuth2ClientCredentialsGrantRequest; @@ -32,6 +33,7 @@ import org.springframework.security.oauth2.client.registration.ClientRegistratio import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository; import org.springframework.security.oauth2.core.OAuth2AccessToken; +import org.springframework.security.oauth2.core.TestOAuth2AccessTokens; import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse; import org.springframework.test.web.servlet.MockMvc; import org.springframework.web.bind.annotation.GetMapping; @@ -41,7 +43,14 @@ import org.springframework.web.servlet.config.annotation.EnableWebMvc; import javax.servlet.http.HttpServletRequest; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; import static org.springframework.security.oauth2.client.registration.TestClientRegistrations.clientCredentials; import static org.springframework.security.oauth2.client.registration.TestClientRegistrations.clientRegistration; import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication; @@ -314,4 +323,71 @@ public class OAuth2ClientConfigurationTests { return mock(OAuth2AccessTokenResponseClient.class); } } + + // gh-8700 + @Test + public void requestWhenAuthorizedClientManagerConfiguredThenUsed() throws Exception { + String clientRegistrationId = "client1"; + String principalName = "user1"; + TestingAuthenticationToken authentication = new TestingAuthenticationToken(principalName, "password"); + + ClientRegistrationRepository clientRegistrationRepository = mock(ClientRegistrationRepository.class); + OAuth2AuthorizedClientRepository authorizedClientRepository = mock(OAuth2AuthorizedClientRepository.class); + OAuth2AuthorizedClientManager authorizedClientManager = mock(OAuth2AuthorizedClientManager.class); + + ClientRegistration clientRegistration = clientRegistration().registrationId(clientRegistrationId).build(); + OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient( + clientRegistration, principalName, TestOAuth2AccessTokens.noScopes()); + + when(authorizedClientManager.authorize(any())).thenReturn(authorizedClient); + + OAuth2AuthorizedClientManagerRegisteredConfig.CLIENT_REGISTRATION_REPOSITORY = clientRegistrationRepository; + OAuth2AuthorizedClientManagerRegisteredConfig.AUTHORIZED_CLIENT_REPOSITORY = authorizedClientRepository; + OAuth2AuthorizedClientManagerRegisteredConfig.AUTHORIZED_CLIENT_MANAGER = authorizedClientManager; + this.spring.register(OAuth2AuthorizedClientManagerRegisteredConfig.class).autowire(); + + this.mockMvc.perform(get("/authorized-client").with(authentication(authentication))) + .andExpect(status().isOk()) + .andExpect(content().string("resolved")); + + verify(authorizedClientManager).authorize(any()); + verifyNoInteractions(clientRegistrationRepository); + verifyNoInteractions(authorizedClientRepository); + } + + @EnableWebMvc + @EnableWebSecurity + static class OAuth2AuthorizedClientManagerRegisteredConfig extends WebSecurityConfigurerAdapter { + static ClientRegistrationRepository CLIENT_REGISTRATION_REPOSITORY; + static OAuth2AuthorizedClientRepository AUTHORIZED_CLIENT_REPOSITORY; + static OAuth2AuthorizedClientManager AUTHORIZED_CLIENT_MANAGER; + + @Override + protected void configure(HttpSecurity http) { + } + + @RestController + public class Controller { + + @GetMapping("/authorized-client") + public String authorizedClient(@RegisteredOAuth2AuthorizedClient("client1") OAuth2AuthorizedClient authorizedClient) { + return authorizedClient != null ? "resolved" : "not-resolved"; + } + } + + @Bean + public ClientRegistrationRepository clientRegistrationRepository() { + return CLIENT_REGISTRATION_REPOSITORY; + } + + @Bean + public OAuth2AuthorizedClientRepository authorizedClientRepository() { + return AUTHORIZED_CLIENT_REPOSITORY; + } + + @Bean + public OAuth2AuthorizedClientManager authorizedClientManager() { + return AUTHORIZED_CLIENT_MANAGER; + } + } }