|
@@ -64,6 +64,8 @@ import org.springframework.security.oauth2.client.registration.ClientRegistratio
|
|
|
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;
|
|
@@ -84,6 +86,7 @@ import org.springframework.security.oauth2.core.oidc.IdTokenClaimNames;
|
|
|
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;
|
|
@@ -664,6 +667,41 @@ public class OAuth2LoginTests {
|
|
|
.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);
|
|
@@ -674,6 +712,51 @@ public class OAuth2LoginTests {
|
|
|
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 {
|