Skip to content

Commit a00ad37

Browse files
committed
OAuth2LoginConfigurer UserService Beans
Fixes gh-7232
1 parent f5cd0ec commit a00ad37

File tree

3 files changed

+123
-18
lines changed

3 files changed

+123
-18
lines changed

config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -2044,7 +2044,7 @@ public OAuth2LoginConfigurer<HttpSecurity> oauth2Login() throws Exception {
20442044
* @throws Exception
20452045
*/
20462046
public HttpSecurity oauth2Login(Customizer<OAuth2LoginConfigurer<HttpSecurity>> oauth2LoginCustomizer) throws Exception {
2047-
oauth2LoginCustomizer.customize(getOrApply(new OAuth2LoginConfigurer<>()));
2047+
oauth2LoginCustomizer.customize(getOrApply(new OAuth2LoginConfigurer<>(getContext())));
20482048
return HttpSecurity.this;
20492049
}
20502050

config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurer.java

+49-17
Original file line numberDiff line numberDiff line change
@@ -135,13 +135,19 @@
135135
public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>> extends
136136
AbstractAuthenticationFilterConfigurer<B, OAuth2LoginConfigurer<B>, OAuth2LoginAuthenticationFilter> {
137137

138+
private final ApplicationContext context;
138139
private final AuthorizationEndpointConfig authorizationEndpointConfig = new AuthorizationEndpointConfig();
139140
private final TokenEndpointConfig tokenEndpointConfig = new TokenEndpointConfig();
140141
private final RedirectionEndpointConfig redirectionEndpointConfig = new RedirectionEndpointConfig();
141142
private final UserInfoEndpointConfig userInfoEndpointConfig = new UserInfoEndpointConfig();
142143
private String loginPage;
143144
private String loginProcessingUrl = OAuth2LoginAuthenticationFilter.DEFAULT_FILTER_PROCESSES_URI;
144145

146+
public OAuth2LoginConfigurer(ApplicationContext context) {
147+
Assert.notNull(context, "context cannot be null");
148+
this.context = context;
149+
}
150+
145151
/**
146152
* Sets the repository of client registrations.
147153
*
@@ -506,18 +512,7 @@ public void init(B http) throws Exception {
506512
accessTokenResponseClient = new DefaultAuthorizationCodeTokenResponseClient();
507513
}
508514

509-
OAuth2UserService<OAuth2UserRequest, OAuth2User> oauth2UserService = this.userInfoEndpointConfig.userService;
510-
if (oauth2UserService == null) {
511-
if (!this.userInfoEndpointConfig.customUserTypes.isEmpty()) {
512-
List<OAuth2UserService<OAuth2UserRequest, OAuth2User>> userServices = new ArrayList<>();
513-
userServices.add(new CustomUserTypesOAuth2UserService(this.userInfoEndpointConfig.customUserTypes));
514-
userServices.add(new DefaultOAuth2UserService());
515-
oauth2UserService = new DelegatingOAuth2UserService<>(userServices);
516-
} else {
517-
oauth2UserService = new DefaultOAuth2UserService();
518-
}
519-
}
520-
515+
OAuth2UserService<OAuth2UserRequest, OAuth2User> oauth2UserService = getOAuth2UserService();
521516
OAuth2LoginAuthenticationProvider oauth2LoginAuthenticationProvider =
522517
new OAuth2LoginAuthenticationProvider(accessTokenResponseClient, oauth2UserService);
523518
GrantedAuthoritiesMapper userAuthoritiesMapper = this.getGrantedAuthoritiesMapper();
@@ -530,11 +525,7 @@ public void init(B http) throws Exception {
530525
"org.springframework.security.oauth2.jwt.JwtDecoder", this.getClass().getClassLoader());
531526

532527
if (oidcAuthenticationProviderEnabled) {
533-
OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService = this.userInfoEndpointConfig.oidcUserService;
534-
if (oidcUserService == null) {
535-
oidcUserService = new OidcUserService();
536-
}
537-
528+
OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService = getOidcUserService();
538529
OidcAuthorizationCodeAuthenticationProvider oidcAuthorizationCodeAuthenticationProvider =
539530
new OidcAuthorizationCodeAuthenticationProvider(accessTokenResponseClient, oidcUserService);
540531
JwtDecoderFactory<ClientRegistration> jwtDecoderFactory = this.getJwtDecoderFactoryBean();
@@ -627,6 +618,47 @@ private GrantedAuthoritiesMapper getGrantedAuthoritiesMapperBean() {
627618
return (!grantedAuthoritiesMapperMap.isEmpty() ? grantedAuthoritiesMapperMap.values().iterator().next() : null);
628619
}
629620

621+
private OAuth2UserService<OidcUserRequest, OidcUser> getOidcUserService() {
622+
if (this.userInfoEndpointConfig.oidcUserService != null) {
623+
return this.userInfoEndpointConfig.oidcUserService;
624+
}
625+
ResolvableType type = ResolvableType.forClassWithGenerics(OAuth2UserService.class, OidcUserRequest.class, OidcUser.class);
626+
OAuth2UserService<OidcUserRequest, OidcUser> bean = getBeanOrNull(type);
627+
if (bean == null) {
628+
return new OidcUserService();
629+
}
630+
631+
return bean;
632+
}
633+
634+
private OAuth2UserService<OAuth2UserRequest, OAuth2User> getOAuth2UserService() {
635+
if (this.userInfoEndpointConfig.userService != null) {
636+
return this.userInfoEndpointConfig.userService;
637+
}
638+
ResolvableType type = ResolvableType.forClassWithGenerics(OAuth2UserService.class, OAuth2UserRequest.class, OAuth2User.class);
639+
OAuth2UserService<OAuth2UserRequest, OAuth2User> bean = getBeanOrNull(type);
640+
if (bean == null) {
641+
if (!this.userInfoEndpointConfig.customUserTypes.isEmpty()) {
642+
List<OAuth2UserService<OAuth2UserRequest, OAuth2User>> userServices = new ArrayList<>();
643+
userServices.add(new CustomUserTypesOAuth2UserService(this.userInfoEndpointConfig.customUserTypes));
644+
userServices.add(new DefaultOAuth2UserService());
645+
return new DelegatingOAuth2UserService<>(userServices);
646+
} else {
647+
return new DefaultOAuth2UserService();
648+
}
649+
}
650+
651+
return bean;
652+
}
653+
654+
private <T> T getBeanOrNull(ResolvableType type) {
655+
String[] names = this.context.getBeanNamesForType(type);
656+
if (names.length == 1) {
657+
return (T) this.context.getBean(names[0]);
658+
}
659+
return null;
660+
}
661+
630662
private void initDefaultLoginFilter(B http) {
631663
DefaultLoginPageGeneratingFilter loginPageGeneratingFilter = http.getSharedObject(DefaultLoginPageGeneratingFilter.class);
632664
if (loginPageGeneratingFilter == null || this.isCustomLoginPage()) {

config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurerTests.java

+73
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,32 @@ public void oauth2LoginCustomWithBeanRegistration() throws Exception {
271271
assertThat(authentication.getAuthorities()).last().hasToString("ROLE_OAUTH2_USER");
272272
}
273273

274+
@Test
275+
public void oauth2LoginCustomWithUserServiceBeanRegistration() throws Exception {
276+
// setup application context
277+
loadConfig(OAuth2LoginConfigCustomUserServiceBeanRegistration.class);
278+
279+
// setup authorization request
280+
OAuth2AuthorizationRequest authorizationRequest = createOAuth2AuthorizationRequest();
281+
this.authorizationRequestRepository.saveAuthorizationRequest(
282+
authorizationRequest, this.request, this.response);
283+
284+
// setup authentication parameters
285+
this.request.setParameter("code", "code123");
286+
this.request.setParameter("state", authorizationRequest.getState());
287+
288+
// perform test
289+
this.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain);
290+
291+
// assertions
292+
Authentication authentication = this.securityContextRepository
293+
.loadContext(new HttpRequestResponseHolder(this.request, this.response))
294+
.getAuthentication();
295+
assertThat(authentication.getAuthorities()).hasSize(2);
296+
assertThat(authentication.getAuthorities()).first().hasToString("ROLE_USER");
297+
assertThat(authentication.getAuthorities()).last().hasToString("ROLE_OAUTH2_USER");
298+
}
299+
274300
// gh-5488
275301
@Test
276302
public void oauth2LoginConfigLoginProcessingUrl() throws Exception {
@@ -660,6 +686,53 @@ GrantedAuthoritiesMapper grantedAuthoritiesMapper() {
660686
}
661687
}
662688

689+
@EnableWebSecurity
690+
static class OAuth2LoginConfigCustomUserServiceBeanRegistration extends WebSecurityConfigurerAdapter {
691+
@Override
692+
protected void configure(HttpSecurity http) throws Exception {
693+
http
694+
.authorizeRequests()
695+
.anyRequest().authenticated()
696+
.and()
697+
.securityContext()
698+
.securityContextRepository(securityContextRepository())
699+
.and()
700+
.oauth2Login()
701+
.tokenEndpoint()
702+
.accessTokenResponseClient(createOauth2AccessTokenResponseClient());
703+
}
704+
705+
@Bean
706+
ClientRegistrationRepository clientRegistrationRepository() {
707+
return new InMemoryClientRegistrationRepository(GOOGLE_CLIENT_REGISTRATION);
708+
}
709+
710+
@Bean
711+
GrantedAuthoritiesMapper grantedAuthoritiesMapper() {
712+
return createGrantedAuthoritiesMapper();
713+
}
714+
715+
@Bean
716+
SecurityContextRepository securityContextRepository() {
717+
return new HttpSessionSecurityContextRepository();
718+
}
719+
720+
@Bean
721+
HttpSessionOAuth2AuthorizationRequestRepository oauth2AuthorizationRequestRepository() {
722+
return new HttpSessionOAuth2AuthorizationRequestRepository();
723+
}
724+
725+
@Bean
726+
OAuth2UserService<OAuth2UserRequest, OAuth2User> oauth2UserService() {
727+
return createOauth2UserService();
728+
}
729+
730+
@Bean
731+
OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService() {
732+
return createOidcUserService();
733+
}
734+
}
735+
663736
@EnableWebSecurity
664737
static class OAuth2LoginConfigLoginProcessingUrl extends CommonWebSecurityConfigurerAdapter {
665738
@Override

0 commit comments

Comments
 (0)