16
16
17
17
package org .springframework .security .config .web .server ;
18
18
19
+ import java .time .Instant ;
20
+ import java .util .Collections ;
21
+ import java .util .HashMap ;
22
+ import java .util .Map ;
23
+
19
24
import org .junit .Rule ;
20
25
import org .junit .Test ;
21
26
import org .openqa .selenium .WebDriver ;
27
+ import reactor .core .publisher .Mono ;
28
+
22
29
import org .springframework .beans .factory .annotation .Autowired ;
30
+ import org .springframework .context .ApplicationContext ;
23
31
import org .springframework .context .annotation .Bean ;
24
32
import org .springframework .context .annotation .Configuration ;
25
33
import org .springframework .security .authentication .ReactiveAuthenticationManager ;
26
34
import org .springframework .security .authentication .TestingAuthenticationToken ;
27
35
import org .springframework .security .config .annotation .web .reactive .EnableWebFluxSecurity ;
28
36
import org .springframework .security .config .oauth2 .client .CommonOAuth2Provider ;
29
37
import org .springframework .security .config .test .SpringTestRule ;
38
+ import org .springframework .security .core .Authentication ;
39
+ import org .springframework .security .core .authority .AuthorityUtils ;
40
+ import org .springframework .security .core .context .SecurityContext ;
41
+ import org .springframework .security .core .context .SecurityContextImpl ;
30
42
import org .springframework .security .htmlunit .server .WebTestClientHtmlUnitDriverBuilder ;
43
+ import org .springframework .security .oauth2 .client .authentication .OAuth2AuthenticationToken ;
31
44
import org .springframework .security .oauth2 .client .authentication .OAuth2AuthorizationCodeAuthenticationToken ;
32
45
import org .springframework .security .oauth2 .client .authentication .OAuth2LoginAuthenticationToken ;
33
46
import org .springframework .security .oauth2 .client .endpoint .OAuth2AuthorizationCodeGrantRequest ;
34
47
import org .springframework .security .oauth2 .client .endpoint .ReactiveOAuth2AccessTokenResponseClient ;
35
48
import org .springframework .security .oauth2 .client .oidc .authentication .OidcAuthorizationCodeReactiveAuthenticationManager ;
49
+ import org .springframework .security .oauth2 .client .web .server .oidc .logout .OidcClientInitiatedServerLogoutSuccessHandler ;
36
50
import org .springframework .security .oauth2 .client .oidc .userinfo .OidcUserRequest ;
37
51
import org .springframework .security .oauth2 .client .registration .ClientRegistration ;
38
52
import org .springframework .security .oauth2 .client .registration .InMemoryReactiveClientRegistrationRepository ;
53
+ import org .springframework .security .oauth2 .client .registration .TestClientRegistrations ;
39
54
import org .springframework .security .oauth2 .client .userinfo .ReactiveOAuth2UserService ;
40
55
import org .springframework .security .oauth2 .client .web .server .ServerOAuth2AuthorizationRequestResolver ;
41
56
import org .springframework .security .oauth2 .core .OAuth2AccessToken ;
60
75
import org .springframework .security .web .server .SecurityWebFilterChain ;
61
76
import org .springframework .security .web .server .WebFilterChainProxy ;
62
77
import org .springframework .security .web .server .authentication .ServerAuthenticationConverter ;
78
+ import org .springframework .security .web .server .context .ServerSecurityContextRepository ;
63
79
import org .springframework .security .web .server .util .matcher .ServerWebExchangeMatcher ;
64
80
import org .springframework .test .web .reactive .server .WebTestClient ;
81
+ import org .springframework .web .reactive .config .EnableWebFlux ;
65
82
import org .springframework .web .server .ServerWebExchange ;
66
83
import org .springframework .web .server .WebFilter ;
67
84
import org .springframework .web .server .WebFilterChain ;
68
- import reactor .core .publisher .Mono ;
69
-
70
- import java .time .Instant ;
71
- import java .util .Collections ;
72
- import java .util .HashMap ;
73
- import java .util .Map ;
85
+ import org .springframework .web .server .WebHandler ;
74
86
75
87
import static org .assertj .core .api .Assertions .assertThat ;
76
88
import static org .mockito .ArgumentMatchers .any ;
77
- import static org .mockito .Mockito .*;
89
+ import static org .mockito .Mockito .mock ;
90
+ import static org .mockito .Mockito .spy ;
91
+ import static org .mockito .Mockito .verify ;
92
+ import static org .mockito .Mockito .when ;
78
93
79
94
/**
80
95
* @author Rob Winch
@@ -85,6 +100,8 @@ public class OAuth2LoginTests {
85
100
@ Rule
86
101
public final SpringTestRule spring = new SpringTestRule ();
87
102
103
+ private WebTestClient client ;
104
+
88
105
@ Autowired
89
106
private WebFilterChainProxy springSecurity ;
90
107
@@ -100,6 +117,14 @@ public class OAuth2LoginTests {
100
117
.clientSecret ("secret" )
101
118
.build ();
102
119
120
+ @ Autowired
121
+ public void setApplicationContext (ApplicationContext context ) {
122
+ if (context .getBeanNamesForType (WebHandler .class ).length > 0 ) {
123
+ this .client = WebTestClient .bindToApplicationContext (context )
124
+ .build ();
125
+ }
126
+ }
127
+
103
128
@ Test
104
129
public void defaultLoginPageWithMultipleClientRegistrationsThenLinks () {
105
130
this .spring .register (OAuth2LoginWithMultipleClientRegistrations .class ).autowire ();
@@ -326,6 +351,60 @@ private ReactiveJwtDecoder getJwtDecoder() {
326
351
}
327
352
}
328
353
354
+
355
+ @ Test
356
+ public void logoutWhenUsingOidcLogoutHandlerThenRedirects () throws Exception {
357
+ this .spring .register (OAuth2LoginConfigWithOidcLogoutSuccessHandler .class ).autowire ();
358
+
359
+ OAuth2AuthenticationToken token = new OAuth2AuthenticationToken (
360
+ TestOidcUsers .create (),
361
+ AuthorityUtils .NO_AUTHORITIES ,
362
+ getBean (ClientRegistration .class ).getRegistrationId ());
363
+
364
+ ServerSecurityContextRepository repository = getBean (ServerSecurityContextRepository .class );
365
+ when (repository .load (any ())).thenReturn (authentication (token ));
366
+
367
+ this .client .post ().uri ("/logout" )
368
+ .exchange ()
369
+ .expectHeader ().valueEquals ("Location" , "http://logout?id_token_hint=id-token" );
370
+ }
371
+
372
+ @ EnableWebFlux
373
+ @ EnableWebFluxSecurity
374
+ static class OAuth2LoginConfigWithOidcLogoutSuccessHandler {
375
+ private final ServerSecurityContextRepository repository =
376
+ mock (ServerSecurityContextRepository .class );
377
+ private final ClientRegistration withLogout =
378
+ TestClientRegistrations .clientRegistration ()
379
+ .providerConfigurationMetadata (Collections .singletonMap (
380
+ "end_session_endpoint" , "http://logout" )).build ();
381
+
382
+ @ Bean
383
+ public SecurityWebFilterChain springSecurity (ServerHttpSecurity http ) {
384
+
385
+ http
386
+ .csrf ().disable ()
387
+ .logout ()
388
+ .logoutSuccessHandler (
389
+ new OidcClientInitiatedServerLogoutSuccessHandler (
390
+ new InMemoryReactiveClientRegistrationRepository (this .withLogout )))
391
+ .and ()
392
+ .securityContextRepository (this .repository );
393
+
394
+ return http .build ();
395
+ }
396
+
397
+ @ Bean
398
+ ServerSecurityContextRepository securityContextRepository () {
399
+ return this .repository ;
400
+ }
401
+
402
+ @ Bean
403
+ ClientRegistration clientRegistration () {
404
+ return this .withLogout ;
405
+ }
406
+ }
407
+
329
408
static class GitHubWebFilter implements WebFilter {
330
409
331
410
@ Override
@@ -336,4 +415,14 @@ public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
336
415
return chain .filter (exchange );
337
416
}
338
417
}
418
+
419
+ Mono <SecurityContext > authentication (Authentication authentication ) {
420
+ SecurityContext context = new SecurityContextImpl ();
421
+ context .setAuthentication (authentication );
422
+ return Mono .just (context );
423
+ }
424
+
425
+ <T > T getBean (Class <T > beanClass ) {
426
+ return this .spring .getContext ().getBean (beanClass );
427
+ }
339
428
}
0 commit comments