35
35
import org .assertj .core .api .InstanceOfAssertFactories ;
36
36
import org .junit .jupiter .api .AfterEach ;
37
37
import org .junit .jupiter .api .Test ;
38
+ import org .mockito .InOrder ;
38
39
import reactor .core .publisher .Mono ;
39
40
40
41
import org .springframework .boot .autoconfigure .AutoConfigurations ;
43
44
import org .springframework .boot .test .context .runner .ReactiveWebApplicationContextRunner ;
44
45
import org .springframework .context .annotation .Bean ;
45
46
import org .springframework .context .annotation .Configuration ;
47
+ import org .springframework .core .annotation .Order ;
46
48
import org .springframework .http .HttpHeaders ;
47
49
import org .springframework .http .HttpStatus ;
48
50
import org .springframework .http .MediaType ;
72
74
import org .springframework .web .server .WebFilter ;
73
75
74
76
import static org .assertj .core .api .Assertions .assertThat ;
77
+ import static org .mockito .ArgumentMatchers .any ;
78
+ import static org .mockito .Mockito .inOrder ;
75
79
import static org .mockito .Mockito .mock ;
76
80
import static org .springframework .security .config .Customizer .withDefaults ;
77
81
@@ -92,7 +96,7 @@ class ReactiveOAuth2ResourceServerAutoConfigurationTests {
92
96
93
97
private MockWebServer server ;
94
98
95
- private static final Duration TIMEOUT = Duration .ofSeconds (5 );
99
+ private static final Duration TIMEOUT = Duration .ofSeconds (5000000 );
96
100
97
101
private static final String JWK_SET = "{\" keys\" :[{\" kty\" :\" RSA\" ,\" e\" :\" AQAB\" ,\" use\" :\" sig\" ,"
98
102
+ "\" kid\" :\" one\" ,\" n\" :\" oXJ8OyOv_eRnce4akdanR4KYRfnC2zLV4uYNQpcFn6oHL0dj7D6kxQmsXoYgJV8ZVDn71KGm"
@@ -127,9 +131,21 @@ void autoConfigurationUsingJwkSetUriShouldConfigureResourceServerUsingSingleJwsA
127
131
assertThat (nimbusReactiveJwtDecoder ).extracting ("jwtProcessor.arg$1.signatureAlgorithms" )
128
132
.asInstanceOf (InstanceOfAssertFactories .collection (SignatureAlgorithm .class ))
129
133
.containsExactlyInAnyOrder (SignatureAlgorithm .RS512 );
134
+ assertJwkSetUriReactiveJwtDecoderBuilderCustomization (context );
130
135
});
131
136
}
132
137
138
+ private void assertJwkSetUriReactiveJwtDecoderBuilderCustomization (
139
+ AssertableReactiveWebApplicationContext context ) {
140
+ JwkSetUriReactiveJwtDecoderBuilderCustomizer customizer = context .getBean ("decoderBuilderCustomizer" ,
141
+ JwkSetUriReactiveJwtDecoderBuilderCustomizer .class );
142
+ JwkSetUriReactiveJwtDecoderBuilderCustomizer anotherCustomizer = context
143
+ .getBean ("anotherDecoderBuilderCustomizer" , JwkSetUriReactiveJwtDecoderBuilderCustomizer .class );
144
+ InOrder inOrder = inOrder (customizer , anotherCustomizer );
145
+ inOrder .verify (customizer ).customize (any ());
146
+ inOrder .verify (anotherCustomizer ).customize (any ());
147
+ }
148
+
133
149
@ Test
134
150
void autoConfigurationUsingJwkSetUriShouldConfigureResourceServerUsingMultipleJwsAlgorithms () {
135
151
this .contextRunner
@@ -141,6 +157,7 @@ void autoConfigurationUsingJwkSetUriShouldConfigureResourceServerUsingMultipleJw
141
157
.asInstanceOf (InstanceOfAssertFactories .collection (SignatureAlgorithm .class ))
142
158
.containsExactlyInAnyOrder (SignatureAlgorithm .RS256 , SignatureAlgorithm .RS384 ,
143
159
SignatureAlgorithm .RS512 );
160
+ assertJwkSetUriReactiveJwtDecoderBuilderCustomization (context );
144
161
});
145
162
}
146
163
@@ -172,7 +189,6 @@ void autoConfigurationUsingPublicKeyValueWithMultipleJwsAlgorithmsShouldFail() {
172
189
}
173
190
174
191
@ Test
175
- @ SuppressWarnings ("unchecked" )
176
192
void autoConfigurationShouldConfigureResourceServerUsingOidcIssuerUri () throws IOException {
177
193
this .server = new MockWebServer ();
178
194
this .server .start ();
@@ -187,18 +203,32 @@ void autoConfigurationShouldConfigureResourceServerUsingOidcIssuerUri() throws I
187
203
assertThat (context ).hasSingleBean (SupplierReactiveJwtDecoder .class );
188
204
assertFilterConfiguredWithJwtAuthenticationManager (context );
189
205
assertThat (context .containsBean ("jwtDecoderByIssuerUri" )).isTrue ();
190
- SupplierReactiveJwtDecoder supplierReactiveJwtDecoder = context
191
- .getBean (SupplierReactiveJwtDecoder .class );
192
- Mono <ReactiveJwtDecoder > reactiveJwtDecoderSupplier = (Mono <ReactiveJwtDecoder >) ReflectionTestUtils
193
- .getField (supplierReactiveJwtDecoder , "jwtDecoderMono" );
194
- reactiveJwtDecoderSupplier .block (TIMEOUT );
206
+ // Trigger calls to the issuer by decoding a token
207
+ decodeJwt (context );
208
+ assertJwkSetUriReactiveJwtDecoderBuilderCustomization (context );
195
209
});
196
210
// The last request is to the JWK Set endpoint to look up the algorithm
197
- assertThat (this .server .getRequestCount ()).isOne ( );
211
+ assertThat (this .server .getRequestCount ()).isEqualTo ( 2 );
198
212
}
199
213
200
- @ Test
201
214
@ SuppressWarnings ("unchecked" )
215
+ private void decodeJwt (AssertableReactiveWebApplicationContext context ) {
216
+ SupplierReactiveJwtDecoder supplierReactiveJwtDecoder = context .getBean (SupplierReactiveJwtDecoder .class );
217
+ Mono <ReactiveJwtDecoder > reactiveJwtDecoderSupplier = (Mono <ReactiveJwtDecoder >) ReflectionTestUtils
218
+ .getField (supplierReactiveJwtDecoder , "jwtDecoderMono" );
219
+ try {
220
+ reactiveJwtDecoderSupplier .flatMap ((decoder ) -> decoder .decode ("eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9."
221
+ + "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0."
222
+ + "NHVaYe26MbtOYhSKkoKYdFVomg4i8ZJd8_-RU8VNbftc4TSMb4bXP3l3YlNWACwyXPGffz5aXHc6lty1Y2t4SWRqGteragsVdZufDn5BlnJl9pdR_kdVFUsra2rWKEofkZeIC4yWytE58sMIihvo9H1ScmmVwBcQP6XETqYd0aSHp1gOa9RdUPDvoXQ5oqygTqVtxaDr6wUFKrKItgBMzWIdNZ6y7O9E0DhEPTbE9rfBo6KTFsHAZnMg4k68CDp2woYIaXbmYTWcvbzIuHO7_37GT79XdIwkm95QJ7hYC9RiwrV7mesbY4PAahERJawntho0my942XheVLmGwLMBkQ" ))
223
+ .block (TIMEOUT );
224
+ }
225
+ catch (Exception ex ) {
226
+ // This fails, but it's enough to check that the expected HTTP calls
227
+ // are made
228
+ }
229
+ }
230
+
231
+ @ Test
202
232
void autoConfigurationShouldConfigureResourceServerUsingOidcRfc8414IssuerUri () throws Exception {
203
233
this .server = new MockWebServer ();
204
234
this .server .start ();
@@ -212,18 +242,15 @@ void autoConfigurationShouldConfigureResourceServerUsingOidcRfc8414IssuerUri() t
212
242
assertThat (context ).hasSingleBean (SupplierReactiveJwtDecoder .class );
213
243
assertFilterConfiguredWithJwtAuthenticationManager (context );
214
244
assertThat (context .containsBean ("jwtDecoderByIssuerUri" )).isTrue ();
215
- SupplierReactiveJwtDecoder supplierReactiveJwtDecoder = context
216
- .getBean (SupplierReactiveJwtDecoder .class );
217
- Mono <ReactiveJwtDecoder > reactiveJwtDecoderSupplier = (Mono <ReactiveJwtDecoder >) ReflectionTestUtils
218
- .getField (supplierReactiveJwtDecoder , "jwtDecoderMono" );
219
- reactiveJwtDecoderSupplier .block (TIMEOUT );
245
+ // Trigger calls to the issuer by decoding a token
246
+ decodeJwt (context );
247
+ // assertJwkSetUriReactiveJwtDecoderBuilderCustomization(context);
220
248
});
221
249
// The last request is to the JWK Set endpoint to look up the algorithm
222
- assertThat (this .server .getRequestCount ()).isEqualTo (2 );
250
+ assertThat (this .server .getRequestCount ()).isEqualTo (3 );
223
251
}
224
252
225
253
@ Test
226
- @ SuppressWarnings ("unchecked" )
227
254
void autoConfigurationShouldConfigureResourceServerUsingOAuthIssuerUri () throws Exception {
228
255
this .server = new MockWebServer ();
229
256
this .server .start ();
@@ -237,14 +264,12 @@ void autoConfigurationShouldConfigureResourceServerUsingOAuthIssuerUri() throws
237
264
assertThat (context ).hasSingleBean (SupplierReactiveJwtDecoder .class );
238
265
assertFilterConfiguredWithJwtAuthenticationManager (context );
239
266
assertThat (context .containsBean ("jwtDecoderByIssuerUri" )).isTrue ();
240
- SupplierReactiveJwtDecoder supplierReactiveJwtDecoder = context
241
- .getBean (SupplierReactiveJwtDecoder .class );
242
- Mono <ReactiveJwtDecoder > reactiveJwtDecoderSupplier = (Mono <ReactiveJwtDecoder >) ReflectionTestUtils
243
- .getField (supplierReactiveJwtDecoder , "jwtDecoderMono" );
244
- reactiveJwtDecoderSupplier .block (TIMEOUT );
267
+ // Trigger calls to the issuer by decoding a token
268
+ decodeJwt (context );
269
+ assertJwkSetUriReactiveJwtDecoderBuilderCustomization (context );
245
270
});
246
271
// The last request is to the JWK Set endpoint to look up the algorithm
247
- assertThat (this .server .getRequestCount ()).isEqualTo (3 );
272
+ assertThat (this .server .getRequestCount ()).isEqualTo (4 );
248
273
}
249
274
250
275
@ Test
@@ -666,6 +691,18 @@ MapReactiveUserDetailsService userDetailsService() {
666
691
return mock (MapReactiveUserDetailsService .class );
667
692
}
668
693
694
+ @ Bean
695
+ @ Order (1 )
696
+ JwkSetUriReactiveJwtDecoderBuilderCustomizer decoderBuilderCustomizer () {
697
+ return mock (JwkSetUriReactiveJwtDecoderBuilderCustomizer .class );
698
+ }
699
+
700
+ @ Bean
701
+ @ Order (2 )
702
+ JwkSetUriReactiveJwtDecoderBuilderCustomizer anotherDecoderBuilderCustomizer () {
703
+ return mock (JwkSetUriReactiveJwtDecoderBuilderCustomizer .class );
704
+ }
705
+
669
706
}
670
707
671
708
@ Configuration (proxyBeanMethods = false )
0 commit comments