|
16 | 16 |
|
17 | 17 | package org.springframework.security.config.annotation.web.configurers.oauth2.client;
|
18 | 18 |
|
19 |
| -import java.io.IOException; |
20 |
| -import java.security.KeyPair; |
21 |
| -import java.security.KeyPairGenerator; |
22 |
| -import java.security.interfaces.RSAPublicKey; |
23 |
| -import java.time.Instant; |
24 |
| -import java.util.List; |
25 |
| -import java.util.Map; |
26 |
| -import java.util.concurrent.ConcurrentHashMap; |
27 |
| -import java.util.function.Consumer; |
28 |
| - |
29 | 19 | import com.nimbusds.jose.jwk.JWKSet;
|
30 | 20 | import com.nimbusds.jose.jwk.RSAKey;
|
31 | 21 | import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
|
|
44 | 34 | import org.htmlunit.util.UrlUtils;
|
45 | 35 | import org.junit.jupiter.api.Test;
|
46 | 36 | import org.junit.jupiter.api.extension.ExtendWith;
|
47 |
| - |
48 | 37 | import org.springframework.beans.factory.ObjectProvider;
|
49 | 38 | import org.springframework.beans.factory.annotation.Autowired;
|
50 | 39 | import org.springframework.context.annotation.Bean;
|
|
75 | 64 | import org.springframework.security.oauth2.core.oidc.TestOidcIdTokens;
|
76 | 65 | import org.springframework.security.oauth2.core.oidc.user.OidcUser;
|
77 | 66 | import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
|
78 |
| -import org.springframework.security.oauth2.jwt.JwsHeader; |
79 |
| -import org.springframework.security.oauth2.jwt.JwtClaimsSet; |
80 |
| -import org.springframework.security.oauth2.jwt.JwtEncoder; |
81 |
| -import org.springframework.security.oauth2.jwt.JwtEncoderParameters; |
82 |
| -import org.springframework.security.oauth2.jwt.NimbusJwtEncoder; |
| 67 | +import org.springframework.security.oauth2.jwt.*; |
83 | 68 | import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
84 | 69 | import org.springframework.security.web.SecurityFilterChain;
|
85 | 70 | import org.springframework.security.web.authentication.logout.LogoutHandler;
|
|
92 | 77 | import org.springframework.web.bind.annotation.RestController;
|
93 | 78 | import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
94 | 79 |
|
| 80 | +import java.io.IOException; |
| 81 | +import java.security.KeyPair; |
| 82 | +import java.security.KeyPairGenerator; |
| 83 | +import java.security.interfaces.RSAPublicKey; |
| 84 | +import java.time.Instant; |
| 85 | +import java.util.List; |
| 86 | +import java.util.Map; |
| 87 | +import java.util.concurrent.ConcurrentHashMap; |
| 88 | +import java.util.function.Consumer; |
| 89 | + |
95 | 90 | import static org.assertj.core.api.Assertions.assertThat;
|
96 | 91 | import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
97 | 92 | import static org.hamcrest.Matchers.containsString;
|
98 | 93 | import static org.mockito.ArgumentMatchers.any;
|
99 | 94 | import static org.mockito.BDDMockito.willThrow;
|
100 |
| -import static org.mockito.Mockito.mock; |
101 |
| -import static org.mockito.Mockito.spy; |
102 |
| -import static org.mockito.Mockito.verify; |
| 95 | +import static org.mockito.Mockito.*; |
103 | 96 | import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
|
104 | 97 | import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;
|
105 | 98 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
@@ -312,6 +305,24 @@ void logoutWhenProviderIssuerMissingThenThrowIllegalArgumentException() throws E
|
312 | 305 | .param("logout_token", logoutToken)));
|
313 | 306 | }
|
314 | 307 |
|
| 308 | + @Test |
| 309 | + void oidcBackChannelLogoutWhenDefaultsThenRemotelyInvalidatesSessions() throws Exception { |
| 310 | + this.spring.register(WebServerConfig.class, OidcProviderConfig.class, WithOidcBackChannelDslConfig.class) |
| 311 | + .autowire(); |
| 312 | + String registrationId = this.clientRegistration.getRegistrationId(); |
| 313 | + MockHttpSession session = login(); |
| 314 | + String logoutToken = this.mvc.perform(get("/token/logout").session(session)) |
| 315 | + .andExpect(status().isOk()) |
| 316 | + .andReturn() |
| 317 | + .getResponse() |
| 318 | + .getContentAsString(); |
| 319 | + this.mvc.perform(post(this.web.url("/logout/connect/back-channel/" + registrationId).toString()) |
| 320 | + .param("logout_token", logoutToken)) |
| 321 | + .andExpect(status().isOk()); |
| 322 | + this.mvc.perform(get("/token/logout").session(session)) |
| 323 | + .andExpect(status().isUnauthorized()); |
| 324 | + } |
| 325 | + |
315 | 326 | private MockHttpSession login() throws Exception {
|
316 | 327 | MockMvcDispatcher dispatcher = (MockMvcDispatcher) this.web.getDispatcher();
|
317 | 328 | this.mvc.perform(get("/token/logout")).andExpect(status().isUnauthorized());
|
@@ -739,6 +750,23 @@ void shutdown() throws IOException {
|
739 | 750 |
|
740 | 751 | }
|
741 | 752 |
|
| 753 | + @Configuration |
| 754 | + @EnableWebSecurity |
| 755 | + @Import(RegistrationConfig.class) |
| 756 | + static class WithOidcBackChannelDslConfig { |
| 757 | + |
| 758 | + @Bean |
| 759 | + @Order(1) |
| 760 | + SecurityFilterChain filters(HttpSecurity http) throws Exception { |
| 761 | + http |
| 762 | + .authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated()) |
| 763 | + .oauth2Login(Customizer.withDefaults()) |
| 764 | + .oidcBackChannelLogout(Customizer.withDefaults()); |
| 765 | + return http.build(); |
| 766 | + } |
| 767 | + |
| 768 | + } |
| 769 | + |
742 | 770 | private static class MockMvcDispatcher extends Dispatcher {
|
743 | 771 |
|
744 | 772 | private final Map<String, MockHttpSession> session = new ConcurrentHashMap<>();
|
|
0 commit comments