36
36
import com .nimbusds .jose .crypto .RSASSASigner ;
37
37
import com .nimbusds .jose .jwk .JWKSet ;
38
38
import com .nimbusds .jose .jwk .RSAKey ;
39
+ import com .nimbusds .jose .util .JSONObjectUtils ;
39
40
import jakarta .servlet .http .HttpServletRequest ;
40
41
import net .minidev .json .JSONObject ;
41
42
import okhttp3 .mockwebserver .MockResponse ;
57
58
import org .springframework .beans .factory .xml .BeanDefinitionParserDelegate ;
58
59
import org .springframework .beans .factory .xml .ParserContext ;
59
60
import org .springframework .beans .factory .xml .XmlReaderContext ;
61
+ import org .springframework .core .ParameterizedTypeReference ;
60
62
import org .springframework .core .convert .converter .Converter ;
61
63
import org .springframework .core .io .ClassPathResource ;
62
64
import org .springframework .http .HttpHeaders ;
84
86
import org .springframework .security .oauth2 .jwt .TestJwts ;
85
87
import org .springframework .security .oauth2 .server .resource .authentication .BearerTokenAuthenticationToken ;
86
88
import org .springframework .security .oauth2 .server .resource .authentication .JwtAuthenticationToken ;
87
- import org .springframework .security .oauth2 .server .resource .introspection .NimbusOpaqueTokenIntrospector ;
88
89
import org .springframework .security .oauth2 .server .resource .introspection .OpaqueTokenAuthenticationConverter ;
89
90
import org .springframework .security .oauth2 .server .resource .introspection .OpaqueTokenIntrospector ;
91
+ import org .springframework .security .oauth2 .server .resource .introspection .SpringOpaqueTokenIntrospector ;
90
92
import org .springframework .security .oauth2 .server .resource .web .BearerTokenResolver ;
91
93
import org .springframework .security .test .context .annotation .SecurityTestExecutionListeners ;
92
94
import org .springframework .security .web .authentication .AuthenticationConverter ;
@@ -139,7 +141,7 @@ public class OAuth2ResourceServerBeanDefinitionParserTests {
139
141
@ Test
140
142
public void getWhenValidBearerTokenThenAcceptsRequest () throws Exception {
141
143
this .spring .configLocations (xml ("JwtRestOperations" ), xml ("Jwt" )).autowire ();
142
- mockRestOperations (jwks ("Default" ));
144
+ mockJwksRestOperations (jwks ("Default" ));
143
145
String token = this .token ("ValidNoScopes" );
144
146
// @formatter:off
145
147
this .mvc .perform (get ("/" ).header ("Authorization" , "Bearer " + token ))
@@ -150,7 +152,7 @@ public void getWhenValidBearerTokenThenAcceptsRequest() throws Exception {
150
152
@ Test
151
153
public void getWhenCustomSecurityContextHolderStrategyThenUses () throws Exception {
152
154
this .spring .configLocations (xml ("JwtRestOperations" ), xml ("JwtCustomSecurityContextHolderStrategy" )).autowire ();
153
- mockRestOperations (jwks ("Default" ));
155
+ mockJwksRestOperations (jwks ("Default" ));
154
156
String token = this .token ("ValidNoScopes" );
155
157
// @formatter:off
156
158
this .mvc .perform (get ("/" ).header ("Authorization" , "Bearer " + token ))
@@ -175,7 +177,7 @@ public void getWhenUsingJwkSetUriThenAcceptsRequest() throws Exception {
175
177
@ Test
176
178
public void getWhenExpiredBearerTokenThenInvalidToken () throws Exception {
177
179
this .spring .configLocations (xml ("JwtRestOperations" ), xml ("Jwt" )).autowire ();
178
- mockRestOperations (jwks ("Default" ));
180
+ mockJwksRestOperations (jwks ("Default" ));
179
181
String token = this .token ("Expired" );
180
182
// @formatter:off
181
183
this .mvc .perform (get ("/" ).header ("Authorization" , "Bearer " + token ))
@@ -187,7 +189,7 @@ public void getWhenExpiredBearerTokenThenInvalidToken() throws Exception {
187
189
@ Test
188
190
public void getWhenBadJwkEndpointThen500 () throws Exception {
189
191
this .spring .configLocations (xml ("JwtRestOperations" ), xml ("Jwt" )).autowire ();
190
- mockRestOperations ("malformed" );
192
+ mockJwksRestOperations ("malformed" );
191
193
String token = this .token ("ValidNoScopes" );
192
194
// @formatter:off
193
195
assertThatExceptionOfType (AuthenticationServiceException .class )
@@ -219,7 +221,7 @@ public void getWhenMalformedBearerTokenThenInvalidToken() throws Exception {
219
221
@ Test
220
222
public void getWhenMalformedPayloadThenInvalidToken () throws Exception {
221
223
this .spring .configLocations (xml ("JwtRestOperations" ), xml ("Jwt" )).autowire ();
222
- mockRestOperations (jwks ("Default" ));
224
+ mockJwksRestOperations (jwks ("Default" ));
223
225
String token = this .token ("MalformedPayload" );
224
226
// @formatter:off
225
227
this .mvc .perform (get ("/" ).header ("Authorization" , "Bearer " + token ))
@@ -242,7 +244,7 @@ public void getWhenUnsignedBearerTokenThenInvalidToken() throws Exception {
242
244
@ Test
243
245
public void getWhenBearerTokenBeforeNotBeforeThenInvalidToken () throws Exception {
244
246
this .spring .configLocations (xml ("JwtRestOperations" ), xml ("Jwt" )).autowire ();
245
- this .mockRestOperations (jwks ("Default" ));
247
+ this .mockJwksRestOperations (jwks ("Default" ));
246
248
String token = this .token ("TooEarly" );
247
249
// @formatter:off
248
250
this .mvc .perform (get ("/" ).header ("Authorization" , "Bearer " + token ))
@@ -299,7 +301,7 @@ public void getWhenNoBearerTokenThenUnauthorized() throws Exception {
299
301
@ Test
300
302
public void getWhenSufficientlyScopedBearerTokenThenAcceptsRequest () throws Exception {
301
303
this .spring .configLocations (xml ("JwtRestOperations" ), xml ("Jwt" )).autowire ();
302
- mockRestOperations (jwks ("Default" ));
304
+ mockJwksRestOperations (jwks ("Default" ));
303
305
String token = this .token ("ValidMessageReadScope" );
304
306
// @formatter:off
305
307
this .mvc .perform (get ("/requires-read-scope" ).header ("Authorization" , "Bearer " + token ))
@@ -310,7 +312,7 @@ public void getWhenSufficientlyScopedBearerTokenThenAcceptsRequest() throws Exce
310
312
@ Test
311
313
public void getWhenInsufficientScopeThenInsufficientScopeError () throws Exception {
312
314
this .spring .configLocations (xml ("JwtRestOperations" ), xml ("Jwt" )).autowire ();
313
- mockRestOperations (jwks ("Default" ));
315
+ mockJwksRestOperations (jwks ("Default" ));
314
316
String token = this .token ("ValidNoScopes" );
315
317
// @formatter:off
316
318
this .mvc .perform (get ("/requires-read-scope" ).header ("Authorization" , "Bearer " + token ))
@@ -322,7 +324,7 @@ public void getWhenInsufficientScopeThenInsufficientScopeError() throws Exceptio
322
324
@ Test
323
325
public void getWhenInsufficientScpThenInsufficientScopeError () throws Exception {
324
326
this .spring .configLocations (xml ("JwtRestOperations" ), xml ("Jwt" )).autowire ();
325
- mockRestOperations (jwks ("Default" ));
327
+ mockJwksRestOperations (jwks ("Default" ));
326
328
String token = this .token ("ValidMessageWriteScp" );
327
329
// @formatter:off
328
330
this .mvc .perform (get ("/requires-read-scope" ).header ("Authorization" , "Bearer " + token ))
@@ -334,7 +336,7 @@ public void getWhenInsufficientScpThenInsufficientScopeError() throws Exception
334
336
@ Test
335
337
public void getWhenAuthorizationServerHasNoMatchingKeyThenInvalidToken () throws Exception {
336
338
this .spring .configLocations (xml ("JwtRestOperations" ), xml ("Jwt" )).autowire ();
337
- mockRestOperations (jwks ("Empty" ));
339
+ mockJwksRestOperations (jwks ("Empty" ));
338
340
String token = this .token ("ValidNoScopes" );
339
341
// @formatter:off
340
342
this .mvc .perform (get ("/" ).header ("Authorization" , "Bearer " + token ))
@@ -346,7 +348,7 @@ public void getWhenAuthorizationServerHasNoMatchingKeyThenInvalidToken() throws
346
348
@ Test
347
349
public void getWhenAuthorizationServerHasMultipleMatchingKeysThenOk () throws Exception {
348
350
this .spring .configLocations (xml ("JwtRestOperations" ), xml ("Jwt" )).autowire ();
349
- mockRestOperations (jwks ("TwoKeys" ));
351
+ mockJwksRestOperations (jwks ("TwoKeys" ));
350
352
String token = this .token ("ValidNoScopes" );
351
353
// @formatter:off
352
354
this .mvc .perform (get ("/authenticated" ).header ("Authorization" , "Bearer " + token ))
@@ -357,7 +359,7 @@ public void getWhenAuthorizationServerHasMultipleMatchingKeysThenOk() throws Exc
357
359
@ Test
358
360
public void getWhenKeyMatchesByKidThenOk () throws Exception {
359
361
this .spring .configLocations (xml ("JwtRestOperations" ), xml ("Jwt" )).autowire ();
360
- mockRestOperations (jwks ("TwoKeys" ));
362
+ mockJwksRestOperations (jwks ("TwoKeys" ));
361
363
String token = this .token ("Kid" );
362
364
// @formatter:off
363
365
this .mvc .perform (get ("/authenticated" ).header ("Authorization" , "Bearer " + token ))
@@ -368,7 +370,7 @@ public void getWhenKeyMatchesByKidThenOk() throws Exception {
368
370
@ Test
369
371
public void postWhenValidBearerTokenAndNoCsrfTokenThenOk () throws Exception {
370
372
this .spring .configLocations (xml ("JwtRestOperations" ), xml ("Jwt" )).autowire ();
371
- mockRestOperations (jwks ("Default" ));
373
+ mockJwksRestOperations (jwks ("Default" ));
372
374
String token = this .token ("ValidNoScopes" );
373
375
// @formatter:off
374
376
this .mvc .perform (post ("/authenticated" ).header ("Authorization" , "Bearer " + token ))
@@ -390,7 +392,7 @@ public void postWhenNoBearerTokenThenCsrfDenies() throws Exception {
390
392
@ Test
391
393
public void postWhenExpiredBearerTokenAndNoCsrfThenInvalidToken () throws Exception {
392
394
this .spring .configLocations (xml ("JwtRestOperations" ), xml ("Jwt" )).autowire ();
393
- mockRestOperations (jwks ("Default" ));
395
+ mockJwksRestOperations (jwks ("Default" ));
394
396
String token = this .token ("Expired" );
395
397
// @formatter:off
396
398
this .mvc .perform (post ("/authenticated" ).header ("Authorization" , "Bearer " + token ))
@@ -402,7 +404,7 @@ public void postWhenExpiredBearerTokenAndNoCsrfThenInvalidToken() throws Excepti
402
404
@ Test
403
405
public void requestWhenJwtThenSessionIsNotCreated () throws Exception {
404
406
this .spring .configLocations (xml ("JwtRestOperations" ), xml ("Jwt" )).autowire ();
405
- mockRestOperations (jwks ("Default" ));
407
+ mockJwksRestOperations (jwks ("Default" ));
406
408
String token = this .token ("ValidNoScopes" );
407
409
// @formatter:off
408
410
MvcResult result = this .mvc .perform (get ("/" ).header ("Authorization" , "Bearer " + token ))
@@ -438,7 +440,7 @@ public void requestWhenNoBearerTokenThenSessionIsCreated() throws Exception {
438
440
@ Test
439
441
public void requestWhenSessionManagementConfiguredThenUses () throws Exception {
440
442
this .spring .configLocations (xml ("JwtRestOperations" ), xml ("AlwaysSessionCreation" )).autowire ();
441
- mockRestOperations (jwks ("Default" ));
443
+ mockJwksRestOperations (jwks ("Default" ));
442
444
String token = this .token ("ValidNoScopes" );
443
445
// @formatter:off
444
446
MvcResult result = this .mvc .perform (get ("/" ).header ("Authorization" , "Bearer " + token ))
@@ -587,7 +589,7 @@ public void requestWhenRealmNameConfiguredThenUsesOnAccessDenied() throws Except
587
589
@ Test
588
590
public void requestWhenCustomJwtValidatorFailsThenCorrespondingErrorMessage () throws Exception {
589
591
this .spring .configLocations (xml ("MockJwtValidator" ), xml ("Jwt" )).autowire ();
590
- mockRestOperations (jwks ("Default" ));
592
+ mockJwksRestOperations (jwks ("Default" ));
591
593
String token = this .token ("ValidNoScopes" );
592
594
OAuth2TokenValidator <Jwt > jwtValidator = this .spring .getContext ().getBean (OAuth2TokenValidator .class );
593
595
OAuth2Error error = new OAuth2Error ("custom-error" , "custom-description" , "custom-uri" );
@@ -602,7 +604,7 @@ public void requestWhenCustomJwtValidatorFailsThenCorrespondingErrorMessage() th
602
604
@ Test
603
605
public void requestWhenClockSkewSetThenTimestampWindowRelaxedAccordingly () throws Exception {
604
606
this .spring .configLocations (xml ("UnexpiredJwtClockSkew" ), xml ("Jwt" )).autowire ();
605
- mockRestOperations (jwks ("Default" ));
607
+ mockJwksRestOperations (jwks ("Default" ));
606
608
String token = this .token ("ExpiresAt4687177990" );
607
609
// @formatter:off
608
610
this .mvc .perform (get ("/" ).header ("Authorization" , "Bearer " + token ))
@@ -613,7 +615,7 @@ public void requestWhenClockSkewSetThenTimestampWindowRelaxedAccordingly() throw
613
615
@ Test
614
616
public void requestWhenClockSkewSetButJwtStillTooLateThenReportsExpired () throws Exception {
615
617
this .spring .configLocations (xml ("ExpiredJwtClockSkew" ), xml ("Jwt" )).autowire ();
616
- mockRestOperations (jwks ("Default" ));
618
+ mockJwksRestOperations (jwks ("Default" ));
617
619
String token = this .token ("ExpiresAt4687177990" );
618
620
// @formatter:off
619
621
this .mvc .perform (get ("/" ).header ("Authorization" , "Bearer " + token ))
@@ -675,7 +677,7 @@ public void requestWhenUsingPublicKeyAlgorithmDoesNotMatchThenReturnsInvalidToke
675
677
@ Test
676
678
public void getWhenIntrospectingThenOk () throws Exception {
677
679
this .spring .configLocations (xml ("OpaqueTokenRestOperations" ), xml ("OpaqueToken" )).autowire ();
678
- mockRestOperations (json ("Active" ));
680
+ mockJsonRestOperations (json ("Active" ));
679
681
// @formatter:off
680
682
this .mvc .perform (get ("/authenticated" ).header ("Authorization" , "Bearer token" ))
681
683
.andExpect (status ().isNotFound ());
@@ -686,7 +688,7 @@ public void getWhenIntrospectingThenOk() throws Exception {
686
688
public void configureWhenIntrospectingWithAuthenticationConverterThenUses () throws Exception {
687
689
this .spring .configLocations (xml ("OpaqueTokenRestOperations" ), xml ("OpaqueTokenAndAuthenticationConverter" ))
688
690
.autowire ();
689
- mockRestOperations (json ("Active" ));
691
+ mockJsonRestOperations (json ("Active" ));
690
692
OpaqueTokenAuthenticationConverter converter = bean (OpaqueTokenAuthenticationConverter .class );
691
693
given (converter .convert (any (), any ())).willReturn (new TestingAuthenticationToken ("user" , "pass" , "app" ));
692
694
// @formatter:off
@@ -699,7 +701,7 @@ public void configureWhenIntrospectingWithAuthenticationConverterThenUses() thro
699
701
@ Test
700
702
public void getWhenIntrospectionFailsThenUnauthorized () throws Exception {
701
703
this .spring .configLocations (xml ("OpaqueTokenRestOperations" ), xml ("OpaqueToken" )).autowire ();
702
- mockRestOperations (json ("Inactive" ));
704
+ mockJsonRestOperations (json ("Inactive" ));
703
705
// @formatter:off
704
706
MockHttpServletRequestBuilder request = get ("/" )
705
707
.header ("Authorization" , "Bearer token" );
@@ -712,7 +714,7 @@ public void getWhenIntrospectionFailsThenUnauthorized() throws Exception {
712
714
@ Test
713
715
public void getWhenIntrospectionLacksScopeThenForbidden () throws Exception {
714
716
this .spring .configLocations (xml ("OpaqueTokenRestOperations" ), xml ("OpaqueToken" )).autowire ();
715
- mockRestOperations (json ("ActiveNoScopes" ));
717
+ mockJsonRestOperations (json ("ActiveNoScopes" ));
716
718
// @formatter:off
717
719
this .mvc .perform (get ("/requires-read-scope" ).header ("Authorization" , "Bearer token" ))
718
720
.andExpect (status ().isForbidden ())
@@ -818,7 +820,7 @@ public void requestWhenFormLoginAndResourceServerEntryPointsThenSessionCreatedBy
818
820
@ Test
819
821
public void getWhenAlsoUsingHttpBasicThenCorrectProviderEngages () throws Exception {
820
822
this .spring .configLocations (xml ("JwtRestOperations" ), xml ("BasicAndResourceServer" )).autowire ();
821
- mockRestOperations (jwks ("Default" ));
823
+ mockJwksRestOperations (jwks ("Default" ));
822
824
String token = this .token ("ValidNoScopes" );
823
825
// @formatter:off
824
826
this .mvc .perform (get ("/authenticated" ).header ("Authorization" , "Bearer " + token ))
@@ -963,14 +965,29 @@ private void mockWebServer(String response) {
963
965
.setBody (response ));
964
966
}
965
967
966
- private void mockRestOperations (String response ) {
968
+ private void mockJwksRestOperations (String response ) {
967
969
RestOperations rest = this .spring .getContext ().getBean (RestOperations .class );
968
970
HttpHeaders headers = new HttpHeaders ();
969
971
headers .setContentType (MediaType .APPLICATION_JSON );
970
972
ResponseEntity <String > entity = new ResponseEntity <>(response , headers , HttpStatus .OK );
971
973
given (rest .exchange (any (RequestEntity .class ), eq (String .class ))).willReturn (entity );
972
974
}
973
975
976
+ private void mockJsonRestOperations (String response ) {
977
+ try {
978
+ RestOperations rest = this .spring .getContext ().getBean (RestOperations .class );
979
+ HttpHeaders headers = new HttpHeaders ();
980
+ headers .setContentType (MediaType .APPLICATION_JSON );
981
+ ResponseEntity <Map <String , Object >> entity = new ResponseEntity <>(JSONObjectUtils .parse (response ), headers ,
982
+ HttpStatus .OK );
983
+ given (rest .exchange (any (RequestEntity .class ), eq (new ParameterizedTypeReference <Map <String , Object >>() {
984
+ }))).willReturn (entity );
985
+ }
986
+ catch (Exception ex ) {
987
+ throw new IllegalArgumentException (ex );
988
+ }
989
+ }
990
+
974
991
private String json (String name ) throws IOException {
975
992
return resource (name + ".json" );
976
993
}
@@ -1047,7 +1064,7 @@ static class OpaqueTokenIntrospectorFactoryBean implements FactoryBean<OpaqueTok
1047
1064
1048
1065
@ Override
1049
1066
public OpaqueTokenIntrospector getObject () throws Exception {
1050
- return new NimbusOpaqueTokenIntrospector ("https://idp.example.org" , this .rest );
1067
+ return new SpringOpaqueTokenIntrospector ("https://idp.example.org" , this .rest );
1051
1068
}
1052
1069
1053
1070
@ Override
0 commit comments