Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reactive oauth2Login should pick up OAuth2ReactiveUserService bean #15848

Merged
merged 1 commit into from
Oct 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4506,7 +4506,9 @@ private ReactiveOAuth2UserService<OidcUserRequest, OidcUser> getOidcUserService(
if (bean != null) {
return bean;
}
return new OidcReactiveOAuth2UserService();
OidcReactiveOAuth2UserService reactiveOAuth2UserService = new OidcReactiveOAuth2UserService();
kse-music marked this conversation as resolved.
Show resolved Hide resolved
reactiveOAuth2UserService.setOauth2UserService(getOauth2UserService());
return reactiveOAuth2UserService;
}

private ReactiveOAuth2UserService<OAuth2UserRequest, OAuth2User> getOauth2UserService() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@
import org.springframework.security.oauth2.client.registration.InMemoryReactiveClientRegistrationRepository;
import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;
import org.springframework.security.oauth2.client.registration.TestClientRegistrations;
import org.springframework.security.oauth2.client.userinfo.DefaultReactiveOAuth2UserService;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.client.userinfo.ReactiveOAuth2UserService;
import org.springframework.security.oauth2.client.web.server.DefaultServerOAuth2AuthorizationRequestResolver;
import org.springframework.security.oauth2.client.web.server.ServerAuthorizationRequestRepository;
Expand All @@ -84,6 +86,7 @@
import org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
import org.springframework.security.oauth2.core.oidc.user.TestOidcUsers;
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.security.oauth2.core.user.TestOAuth2Users;
import org.springframework.security.oauth2.jwt.Jwt;
Expand Down Expand Up @@ -664,6 +667,41 @@ public void oauth2LoginWhenDefaultsThenNoOidcSessionRegistry() {
.block()).isEmpty();
}

@Test
public void oauth2LoginWhenOauth2UserServiceBeanPresent() {
this.spring.register(OAuth2LoginWithMultipleClientRegistrations.class, OAuth2LoginWithOauth2UserService.class)
.autowire();
WebTestClient webTestClient = WebTestClientBuilder.bindToWebFilters(this.springSecurity).build();
OAuth2LoginWithOauth2UserService config = this.spring.getContext()
.getBean(OAuth2LoginWithOauth2UserService.class);
OAuth2AuthorizationRequest request = TestOAuth2AuthorizationRequests.request().scope("openid").build();
OAuth2AuthorizationResponse response = TestOAuth2AuthorizationResponses.success().build();
OAuth2AuthorizationExchange exchange = new OAuth2AuthorizationExchange(request, response);
OAuth2AccessToken accessToken = TestOAuth2AccessTokens.scopes("openid");
OAuth2AuthorizationCodeAuthenticationToken token = new OAuth2AuthorizationCodeAuthenticationToken(google,
exchange, accessToken);
ServerAuthenticationConverter converter = config.authenticationConverter;
given(converter.convert(any())).willReturn(Mono.just(token));
ServerSecurityContextRepository securityContextRepository = config.securityContextRepository;
given(securityContextRepository.save(any(), any())).willReturn(Mono.empty());
given(securityContextRepository.load(any())).willReturn(authentication(token));
Map<String, Object> additionalParameters = new HashMap<>();
additionalParameters.put(OidcParameterNames.ID_TOKEN, "id-token");
OAuth2AccessTokenResponse accessTokenResponse = OAuth2AccessTokenResponse.withToken(accessToken.getTokenValue())
.tokenType(accessToken.getTokenType())
.scopes(accessToken.getScopes())
.additionalParameters(additionalParameters)
.build();
ReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> tokenResponseClient = config.tokenResponseClient;
given(tokenResponseClient.getTokenResponse(any())).willReturn(Mono.just(accessTokenResponse));
ReactiveOAuth2UserService<OAuth2UserRequest, OAuth2User> userService = config.reactiveOAuth2UserService;
given(userService.loadUser(any())).willReturn(Mono
.just(new DefaultOAuth2User(AuthorityUtils.createAuthorityList("USER"), Map.of("sub", "subject"), "sub")));
webTestClient.get().uri("/login/oauth2/code/google").exchange().expectStatus().is3xxRedirection();
verify(userService).loadUser(any());

}

Mono<SecurityContext> authentication(Authentication authentication) {
SecurityContext context = new SecurityContextImpl();
context.setAuthentication(authentication);
Expand All @@ -674,6 +712,51 @@ <T> T getBean(Class<T> beanClass) {
return this.spring.getContext().getBean(beanClass);
}

@Configuration
static class OAuth2LoginWithOauth2UserService {

ReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> tokenResponseClient = mock(
ReactiveOAuth2AccessTokenResponseClient.class);

ReactiveOAuth2UserService<OAuth2UserRequest, OAuth2User> reactiveOAuth2UserService = mock(
DefaultReactiveOAuth2UserService.class);

ServerAuthenticationConverter authenticationConverter = mock(ServerAuthenticationConverter.class);

ServerSecurityContextRepository securityContextRepository = mock(ServerSecurityContextRepository.class);

@Bean
SecurityWebFilterChain springSecurity(ServerHttpSecurity http) {
http.authorizeExchange((authorize) -> authorize.anyExchange().authenticated())
.oauth2Login((c) -> c.authenticationConverter(this.authenticationConverter)
.securityContextRepository(this.securityContextRepository));
return http.build();
}

@Bean
ReactiveOAuth2UserService<OAuth2UserRequest, OAuth2User> customOAuth2UserService() {
return this.reactiveOAuth2UserService;
}

@Bean
ReactiveJwtDecoderFactory<ClientRegistration> jwtDecoderFactory() {
return (clientRegistration) -> (token) -> {
Map<String, Object> claims = new HashMap<>();
claims.put(IdTokenClaimNames.SUB, "subject");
claims.put(IdTokenClaimNames.ISS, "http://localhost/issuer");
claims.put(IdTokenClaimNames.AUD, Collections.singletonList("client"));
claims.put(IdTokenClaimNames.AZP, "client");
return Mono.just(TestJwts.jwt().claims((c) -> c.putAll(claims)).build());
};
}

@Bean
ReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> requestReactiveOAuth2AccessTokenResponseClient() {
return this.tokenResponseClient;
}

}

@Configuration
@EnableWebFluxSecurity
static class OAuth2LoginWithMultipleClientRegistrations {
Expand Down