19
19
import org .junit .After ;
20
20
import org .junit .Before ;
21
21
import org .junit .Test ;
22
- import org .springframework .beans .PropertyAccessorFactory ;
22
+ import org .springframework .beans .factory . NoUniqueBeanDefinitionException ;
23
23
import org .springframework .beans .factory .annotation .Autowired ;
24
24
import org .springframework .context .ApplicationListener ;
25
25
import org .springframework .context .ConfigurableApplicationContext ;
26
26
import org .springframework .context .annotation .Bean ;
27
+ import org .springframework .context .annotation .Configuration ;
27
28
import org .springframework .http .MediaType ;
28
29
import org .springframework .mock .web .MockFilterChain ;
29
30
import org .springframework .mock .web .MockHttpServletRequest ;
50
51
import org .springframework .security .oauth2 .client .web .AuthorizationRequestRepository ;
51
52
import org .springframework .security .oauth2 .client .web .HttpSessionOAuth2AuthorizationRequestRepository ;
52
53
import org .springframework .security .oauth2 .client .web .OAuth2AuthorizationRequestResolver ;
53
- import org .springframework .security .oauth2 .client .web .OAuth2LoginAuthenticationFilter ;
54
54
import org .springframework .security .oauth2 .core .OAuth2AccessToken ;
55
55
import org .springframework .security .oauth2 .core .endpoint .OAuth2AccessTokenResponse ;
56
56
import org .springframework .security .oauth2 .core .endpoint .OAuth2AuthorizationRequest ;
66
66
import org .springframework .security .oauth2 .core .user .OAuth2UserAuthority ;
67
67
import org .springframework .security .oauth2 .jwt .Jwt ;
68
68
import org .springframework .security .oauth2 .jwt .JwtDecoder ;
69
+ import org .springframework .security .oauth2 .jwt .JwtDecoderFactory ;
69
70
import org .springframework .security .web .FilterChainProxy ;
70
71
import org .springframework .security .web .context .HttpRequestResponseHolder ;
71
72
import org .springframework .security .web .context .HttpSessionSecurityContextRepository ;
81
82
import java .util .Map ;
82
83
83
84
import static org .assertj .core .api .Assertions .assertThat ;
85
+ import static org .assertj .core .api .Assertions .assertThatThrownBy ;
84
86
import static org .mockito .ArgumentMatchers .any ;
85
87
import static org .mockito .Mockito .mock ;
86
88
import static org .mockito .Mockito .when ;
@@ -369,8 +371,7 @@ public void oauth2LoginWithCustomLoginPageThenRedirectCustomLoginPage() throws E
369
371
@ Test
370
372
public void oidcLogin () throws Exception {
371
373
// setup application context
372
- loadConfig (OAuth2LoginConfig .class );
373
- registerJwtDecoder ();
374
+ loadConfig (OAuth2LoginConfig .class , JwtDecoderFactoryConfig .class );
374
375
375
376
// setup authorization request
376
377
OAuth2AuthorizationRequest authorizationRequest = createOAuth2AuthorizationRequest ("openid" );
@@ -396,8 +397,7 @@ public void oidcLogin() throws Exception {
396
397
@ Test
397
398
public void oidcLoginCustomWithConfigurer () throws Exception {
398
399
// setup application context
399
- loadConfig (OAuth2LoginConfigCustomWithConfigurer .class );
400
- registerJwtDecoder ();
400
+ loadConfig (OAuth2LoginConfigCustomWithConfigurer .class , JwtDecoderFactoryConfig .class );
401
401
402
402
// setup authorization request
403
403
OAuth2AuthorizationRequest authorizationRequest = createOAuth2AuthorizationRequest ("openid" );
@@ -423,8 +423,7 @@ public void oidcLoginCustomWithConfigurer() throws Exception {
423
423
@ Test
424
424
public void oidcLoginCustomWithBeanRegistration () throws Exception {
425
425
// setup application context
426
- loadConfig (OAuth2LoginConfigCustomWithBeanRegistration .class );
427
- registerJwtDecoder ();
426
+ loadConfig (OAuth2LoginConfigCustomWithBeanRegistration .class , JwtDecoderFactoryConfig .class );
428
427
429
428
// setup authorization request
430
429
OAuth2AuthorizationRequest authorizationRequest = createOAuth2AuthorizationRequest ("openid" );
@@ -447,6 +446,15 @@ public void oidcLoginCustomWithBeanRegistration() throws Exception {
447
446
assertThat (authentication .getAuthorities ()).last ().hasToString ("ROLE_OIDC_USER" );
448
447
}
449
448
449
+ @ Test
450
+ public void oidcLoginCustomWithNoUniqueJwtDecoderFactory () {
451
+ assertThatThrownBy (() -> loadConfig (OAuth2LoginConfig .class , NoUniqueJwtDecoderFactoryConfig .class ))
452
+ .hasRootCauseInstanceOf (NoUniqueBeanDefinitionException .class )
453
+ .hasMessageContaining ("No qualifying bean of type " +
454
+ "'org.springframework.security.oauth2.jwt.JwtDecoderFactory<org.springframework.security.oauth2.client.registration.ClientRegistration>' " +
455
+ "available: expected single matching bean but found 2: jwtDecoderFactory1,jwtDecoderFactory2" );
456
+ }
457
+
450
458
private void loadConfig (Class <?>... configs ) {
451
459
AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext ();
452
460
applicationContext .register (configs );
@@ -455,25 +463,6 @@ private void loadConfig(Class<?>... configs) {
455
463
this .context = applicationContext ;
456
464
}
457
465
458
- private void registerJwtDecoder () {
459
- JwtDecoder decoder = token -> {
460
- Map <String , Object > claims = new HashMap <>();
461
- claims .put (IdTokenClaimNames .SUB , "sub123" );
462
- claims .put (IdTokenClaimNames .ISS , "http://localhost/iss" );
463
- claims .put (IdTokenClaimNames .AUD , Arrays .asList ("clientId" , "a" , "u" , "d" ));
464
- claims .put (IdTokenClaimNames .AZP , "clientId" );
465
- return new Jwt ("token123" , Instant .now (), Instant .now ().plusSeconds (3600 ),
466
- Collections .singletonMap ("header1" , "value1" ), claims );
467
- };
468
- this .springSecurityFilterChain .getFilters ("/login/oauth2/code/google" ).stream ()
469
- .filter (OAuth2LoginAuthenticationFilter .class ::isInstance )
470
- .findFirst ()
471
- .ifPresent (filter -> PropertyAccessorFactory .forDirectFieldAccess (filter )
472
- .setPropertyValue (
473
- "authenticationManager.providers[2].jwtDecoders['google']" ,
474
- decoder ));
475
- }
476
-
477
466
private OAuth2AuthorizationRequest createOAuth2AuthorizationRequest (String ... scopes ) {
478
467
return this .createOAuth2AuthorizationRequest (GOOGLE_CLIENT_REGISTRATION , scopes );
479
468
}
@@ -632,6 +621,42 @@ HttpSessionOAuth2AuthorizationRequestRepository oauth2AuthorizationRequestReposi
632
621
}
633
622
}
634
623
624
+ @ Configuration
625
+ static class JwtDecoderFactoryConfig {
626
+
627
+ @ Bean
628
+ JwtDecoderFactory <ClientRegistration > jwtDecoderFactory () {
629
+ return clientRegistration -> getJwtDecoder ();
630
+ }
631
+
632
+ private static JwtDecoder getJwtDecoder () {
633
+ return token -> {
634
+ Map <String , Object > claims = new HashMap <>();
635
+ claims .put (IdTokenClaimNames .SUB , "sub123" );
636
+ claims .put (IdTokenClaimNames .ISS , "http://localhost/iss" );
637
+ claims .put (IdTokenClaimNames .AUD , Arrays .asList ("clientId" , "a" , "u" , "d" ));
638
+ claims .put (IdTokenClaimNames .AZP , "clientId" );
639
+ return new Jwt ("token123" , Instant .now (), Instant .now ().plusSeconds (3600 ),
640
+ Collections .singletonMap ("header1" , "value1" ), claims );
641
+ };
642
+ }
643
+ }
644
+
645
+ @ Configuration
646
+ static class NoUniqueJwtDecoderFactoryConfig {
647
+
648
+ @ Bean
649
+ JwtDecoderFactory <ClientRegistration > jwtDecoderFactory1 () {
650
+ return clientRegistration -> JwtDecoderFactoryConfig .getJwtDecoder ();
651
+ }
652
+
653
+ @ Bean
654
+ JwtDecoderFactory <ClientRegistration > jwtDecoderFactory2 () {
655
+ return clientRegistration -> JwtDecoderFactoryConfig .getJwtDecoder ();
656
+ }
657
+
658
+ }
659
+
635
660
private static OAuth2AccessTokenResponseClient <OAuth2AuthorizationCodeGrantRequest > createOauth2AccessTokenResponseClient () {
636
661
return request -> {
637
662
Map <String , Object > additionalParameters = new HashMap <>();
0 commit comments