Skip to content

Commit bfe1e6a

Browse files
fritzdjjgrandja
authored andcommitted
OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> @bean is discovered by OAuth2ClientConfiguration
Fixes gh-6572
1 parent e9e7f7d commit bfe1e6a

File tree

2 files changed

+137
-20
lines changed

2 files changed

+137
-20
lines changed

config/src/main/java/org/springframework/security/config/annotation/web/configuration/OAuth2ClientConfiguration.java

+15-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2019 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.
@@ -15,20 +15,22 @@
1515
*/
1616
package org.springframework.security.config.annotation.web.configuration;
1717

18+
import java.util.List;
19+
import java.util.Optional;
1820
import org.springframework.beans.factory.annotation.Autowired;
1921
import org.springframework.context.annotation.Configuration;
2022
import org.springframework.context.annotation.Import;
2123
import org.springframework.context.annotation.ImportSelector;
2224
import org.springframework.core.type.AnnotationMetadata;
25+
import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;
26+
import org.springframework.security.oauth2.client.endpoint.OAuth2ClientCredentialsGrantRequest;
2327
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
2428
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
2529
import org.springframework.security.oauth2.client.web.method.annotation.OAuth2AuthorizedClientArgumentResolver;
2630
import org.springframework.util.ClassUtils;
2731
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
2832
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
2933

30-
import java.util.List;
31-
3234
/**
3335
* {@link Configuration} for OAuth 2.0 Client support.
3436
*
@@ -60,13 +62,17 @@ public String[] selectImports(AnnotationMetadata importingClassMetadata) {
6062
static class OAuth2ClientWebMvcSecurityConfiguration implements WebMvcConfigurer {
6163
private ClientRegistrationRepository clientRegistrationRepository;
6264
private OAuth2AuthorizedClientRepository authorizedClientRepository;
65+
private OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> accessTokenResponseClient;
6366

6467
@Override
6568
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
6669
if (this.clientRegistrationRepository != null && this.authorizedClientRepository != null) {
6770
OAuth2AuthorizedClientArgumentResolver authorizedClientArgumentResolver =
6871
new OAuth2AuthorizedClientArgumentResolver(
6972
this.clientRegistrationRepository, this.authorizedClientRepository);
73+
if (this.accessTokenResponseClient != null) {
74+
authorizedClientArgumentResolver.setClientCredentialsTokenResponseClient(this.accessTokenResponseClient);
75+
}
7076
argumentResolvers.add(authorizedClientArgumentResolver);
7177
}
7278
}
@@ -84,5 +90,11 @@ public void setAuthorizedClientRepository(List<OAuth2AuthorizedClientRepository>
8490
this.authorizedClientRepository = authorizedClientRepositories.get(0);
8591
}
8692
}
93+
94+
@Autowired
95+
public void setAccessTokenResponseClient(
96+
Optional<OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest>> accessTokenResponseClient) {
97+
accessTokenResponseClient.ifPresent(client -> this.accessTokenResponseClient = client);
98+
}
8799
}
88100
}

config/src/test/java/org/springframework/security/config/annotation/web/configuration/OAuth2ClientConfigurationTests.java

+122-17
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2019 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.
@@ -15,6 +15,21 @@
1515
*/
1616
package org.springframework.security.config.annotation.web.configuration;
1717

18+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
19+
import static org.mockito.ArgumentMatchers.any;
20+
import static org.mockito.ArgumentMatchers.eq;
21+
import static org.mockito.Mockito.mock;
22+
import static org.mockito.Mockito.times;
23+
import static org.mockito.Mockito.verify;
24+
import static org.mockito.Mockito.verifyZeroInteractions;
25+
import static org.mockito.Mockito.when;
26+
import static org.springframework.security.oauth2.client.registration.TestClientRegistrations.clientCredentials;
27+
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;
28+
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
29+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
30+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
31+
32+
import javax.servlet.http.HttpServletRequest;
1833
import org.junit.Rule;
1934
import org.junit.Test;
2035
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
@@ -26,26 +41,18 @@
2641
import org.springframework.security.config.test.SpringTestRule;
2742
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
2843
import org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient;
44+
import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;
45+
import org.springframework.security.oauth2.client.endpoint.OAuth2ClientCredentialsGrantRequest;
46+
import org.springframework.security.oauth2.client.registration.ClientRegistration;
2947
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
3048
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
3149
import org.springframework.security.oauth2.core.OAuth2AccessToken;
50+
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
3251
import org.springframework.test.web.servlet.MockMvc;
3352
import org.springframework.web.bind.annotation.GetMapping;
3453
import org.springframework.web.bind.annotation.RestController;
3554
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
3655

37-
import javax.servlet.http.HttpServletRequest;
38-
39-
import static org.assertj.core.api.Assertions.assertThatThrownBy;
40-
import static org.mockito.ArgumentMatchers.any;
41-
import static org.mockito.ArgumentMatchers.eq;
42-
import static org.mockito.Mockito.mock;
43-
import static org.mockito.Mockito.when;
44-
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;
45-
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
46-
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
47-
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
48-
4956
/**
5057
* Tests for {@link OAuth2ClientConfiguration}.
5158
*
@@ -64,26 +71,66 @@ public void requestWhenAuthorizedClientFoundThenMethodArgumentResolved() throws
6471
String principalName = "user1";
6572
TestingAuthenticationToken authentication = new TestingAuthenticationToken(principalName, "password");
6673

74+
ClientRegistrationRepository clientRegistrationRepository = mock(ClientRegistrationRepository.class);
6775
OAuth2AuthorizedClientRepository authorizedClientRepository = mock(OAuth2AuthorizedClientRepository.class);
6876
OAuth2AuthorizedClient authorizedClient = mock(OAuth2AuthorizedClient.class);
6977
when(authorizedClientRepository.loadAuthorizedClient(
70-
eq(clientRegistrationId), eq(authentication), any(HttpServletRequest.class))).thenReturn(authorizedClient);
78+
eq(clientRegistrationId), eq(authentication), any(HttpServletRequest.class)))
79+
.thenReturn(authorizedClient);
7180

7281
OAuth2AccessToken accessToken = mock(OAuth2AccessToken.class);
7382
when(authorizedClient.getAccessToken()).thenReturn(accessToken);
7483

84+
OAuth2AccessTokenResponseClient accessTokenResponseClient = mock(OAuth2AccessTokenResponseClient.class);
85+
86+
OAuth2AuthorizedClientArgumentResolverConfig.CLIENT_REGISTRATION_REPOSITORY = clientRegistrationRepository;
7587
OAuth2AuthorizedClientArgumentResolverConfig.AUTHORIZED_CLIENT_REPOSITORY = authorizedClientRepository;
88+
OAuth2AuthorizedClientArgumentResolverConfig.ACCESS_TOKEN_RESPONSE_CLIENT = accessTokenResponseClient;
7689
this.spring.register(OAuth2AuthorizedClientArgumentResolverConfig.class).autowire();
7790

7891
this.mockMvc.perform(get("/authorized-client").with(authentication(authentication)))
79-
.andExpect(status().isOk())
80-
.andExpect(content().string("resolved"));
92+
.andExpect(status().isOk())
93+
.andExpect(content().string("resolved"));
94+
verifyZeroInteractions(accessTokenResponseClient);
95+
}
96+
97+
@Test
98+
public void requestWhenAuthorizedClientNotFoundAndClientCredentialsThenTokenResponseClientIsUsed() throws Exception {
99+
String clientRegistrationId = "client1";
100+
String principalName = "user1";
101+
TestingAuthenticationToken authentication = new TestingAuthenticationToken(principalName, "password");
102+
103+
ClientRegistrationRepository clientRegistrationRepository = mock(ClientRegistrationRepository.class);
104+
OAuth2AuthorizedClientRepository authorizedClientRepository = mock(OAuth2AuthorizedClientRepository.class);
105+
OAuth2AccessTokenResponseClient accessTokenResponseClient = mock(OAuth2AccessTokenResponseClient.class);
106+
107+
ClientRegistration clientRegistration = clientCredentials().registrationId(clientRegistrationId).build();
108+
when(clientRegistrationRepository.findByRegistrationId(clientRegistrationId)).thenReturn(clientRegistration);
109+
110+
OAuth2AccessTokenResponse accessTokenResponse = OAuth2AccessTokenResponse.withToken("access-token-1234")
111+
.tokenType(OAuth2AccessToken.TokenType.BEARER)
112+
.expiresIn(300)
113+
.build();
114+
when(accessTokenResponseClient.getTokenResponse(any(OAuth2ClientCredentialsGrantRequest.class)))
115+
.thenReturn(accessTokenResponse);
116+
117+
OAuth2AuthorizedClientArgumentResolverConfig.CLIENT_REGISTRATION_REPOSITORY = clientRegistrationRepository;
118+
OAuth2AuthorizedClientArgumentResolverConfig.AUTHORIZED_CLIENT_REPOSITORY = authorizedClientRepository;
119+
OAuth2AuthorizedClientArgumentResolverConfig.ACCESS_TOKEN_RESPONSE_CLIENT = accessTokenResponseClient;
120+
this.spring.register(OAuth2AuthorizedClientArgumentResolverConfig.class).autowire();
121+
122+
this.mockMvc.perform(get("/authorized-client").with(authentication(authentication)))
123+
.andExpect(status().isOk())
124+
.andExpect(content().string("resolved"));
125+
verify(accessTokenResponseClient, times(1)).getTokenResponse(any(OAuth2ClientCredentialsGrantRequest.class));
81126
}
82127

83128
@EnableWebMvc
84129
@EnableWebSecurity
85130
static class OAuth2AuthorizedClientArgumentResolverConfig extends WebSecurityConfigurerAdapter {
131+
static ClientRegistrationRepository CLIENT_REGISTRATION_REPOSITORY;
86132
static OAuth2AuthorizedClientRepository AUTHORIZED_CLIENT_REPOSITORY;
133+
static OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> ACCESS_TOKEN_RESPONSE_CLIENT;
87134

88135
@Override
89136
protected void configure(HttpSecurity http) throws Exception {
@@ -100,13 +147,18 @@ public String authorizedClient(@RegisteredOAuth2AuthorizedClient("client1") OAut
100147

101148
@Bean
102149
public ClientRegistrationRepository clientRegistrationRepository() {
103-
return mock(ClientRegistrationRepository.class);
150+
return CLIENT_REGISTRATION_REPOSITORY;
104151
}
105152

106153
@Bean
107154
public OAuth2AuthorizedClientRepository authorizedClientRepository() {
108155
return AUTHORIZED_CLIENT_REPOSITORY;
109156
}
157+
158+
@Bean
159+
public OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> accessTokenResponseClient() {
160+
return ACCESS_TOKEN_RESPONSE_CLIENT;
161+
}
110162
}
111163

112164
// gh-5321
@@ -147,6 +199,11 @@ public OAuth2AuthorizedClientRepository authorizedClientRepository1() {
147199
public OAuth2AuthorizedClientRepository authorizedClientRepository2() {
148200
return mock(OAuth2AuthorizedClientRepository.class);
149201
}
202+
203+
@Bean
204+
public OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> accessTokenResponseClient() {
205+
return mock(OAuth2AccessTokenResponseClient.class);
206+
}
150207
}
151208

152209
@Test
@@ -208,5 +265,53 @@ public ClientRegistrationRepository clientRegistrationRepository2() {
208265
public OAuth2AuthorizedClientRepository authorizedClientRepository() {
209266
return mock(OAuth2AuthorizedClientRepository.class);
210267
}
268+
269+
@Bean
270+
public OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> accessTokenResponseClient() {
271+
return mock(OAuth2AccessTokenResponseClient.class);
272+
}
273+
}
274+
275+
@Test
276+
public void loadContextWhenAccessTokenResponseClientRegisteredTwiceThenThrowNoUniqueBeanDefinitionException() {
277+
assertThatThrownBy(() -> this.spring.register(AccessTokenResponseClientRegisteredTwiceConfig.class).autowire())
278+
.hasRootCauseInstanceOf(NoUniqueBeanDefinitionException.class)
279+
.hasMessageContaining("expected single matching bean but found 2: accessTokenResponseClient1,accessTokenResponseClient2");
280+
}
281+
282+
@EnableWebMvc
283+
@EnableWebSecurity
284+
static class AccessTokenResponseClientRegisteredTwiceConfig extends WebSecurityConfigurerAdapter {
285+
286+
@Override
287+
protected void configure(HttpSecurity http) throws Exception {
288+
// @formatter:off
289+
http
290+
.authorizeRequests()
291+
.anyRequest().authenticated()
292+
.and()
293+
.oauth2Login();
294+
// @formatter:on
295+
}
296+
297+
@Bean
298+
public ClientRegistrationRepository clientRegistrationRepository() {
299+
return mock(ClientRegistrationRepository.class);
300+
}
301+
302+
@Bean
303+
public OAuth2AuthorizedClientRepository authorizedClientRepository() {
304+
return mock(OAuth2AuthorizedClientRepository.class);
305+
}
306+
307+
@Bean
308+
public OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> accessTokenResponseClient1() {
309+
return mock(OAuth2AccessTokenResponseClient.class);
310+
}
311+
312+
@Bean
313+
public OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> accessTokenResponseClient2() {
314+
return mock(OAuth2AccessTokenResponseClient.class);
315+
}
211316
}
212317
}

0 commit comments

Comments
 (0)