Skip to content

Commit 6fbab2a

Browse files
committed
Make Oidc Session Logout Cookie Name Configurable
This is specifically helpful for Spring Session support Closes gh-14904
1 parent e423024 commit 6fbab2a

File tree

2 files changed

+94
-0
lines changed

2 files changed

+94
-0
lines changed

config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OidcLogoutConfigurer.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,8 @@ public final class SessionLogoutConfigurer {
310310

311311
private String logoutUri = "{baseScheme}://localhost{basePort}/logout/connect/back-channel/{registrationId}";
312312

313+
private String cookieName = "JSESSIONID";
314+
313315
private SessionLogoutConfigurer() {
314316

315317
}
@@ -339,6 +341,23 @@ public SessionLogoutConfigurer uri(String uri) {
339341
return this;
340342
}
341343

344+
/**
345+
* Use this cookie name to propagate the internal session identifier in the
346+
* internal logout invocation.
347+
*
348+
* <p>
349+
* This defaults to {@code JSESSIONID}.
350+
*
351+
* <p>
352+
* When using Spring Session, you may need to set this to {@code SESSION}
353+
* @param cookieName the cookie name to use
354+
* @return the {@link SessionLogoutConfigurer} for further customizations
355+
*/
356+
public SessionLogoutConfigurer cookieName(String cookieName) {
357+
this.cookieName = cookieName;
358+
return this;
359+
}
360+
342361
private LogoutHandler configure(B http) {
343362
OidcBackChannelLogoutHandler logoutHandler = new OidcBackChannelLogoutHandler();
344363
logoutHandler.setSessionRegistry(OAuth2ClientConfigurerUtils.getOidcSessionRegistry(http));

config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OidcLogoutConfigurerTests.java

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import java.util.List;
2525
import java.util.Map;
2626
import java.util.concurrent.ConcurrentHashMap;
27+
import java.util.function.Consumer;
2728

2829
import com.nimbusds.jose.jwk.JWKSet;
2930
import com.nimbusds.jose.jwk.RSAKey;
@@ -91,6 +92,7 @@
9192
import org.springframework.web.bind.annotation.RestController;
9293
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
9394

95+
import static org.assertj.core.api.Assertions.assertThat;
9496
import static org.hamcrest.Matchers.containsString;
9597
import static org.mockito.ArgumentMatchers.any;
9698
import static org.mockito.BDDMockito.willThrow;
@@ -235,6 +237,23 @@ void logoutWhenSelfRemoteLogoutUriThenUses() throws Exception {
235237
this.mvc.perform(get("/token/logout").session(session)).andExpect(status().isUnauthorized());
236238
}
237239

240+
@Test
241+
void logoutWhenDifferentCookieNameThenUses() throws Exception {
242+
this.spring.register(OidcProviderConfig.class, CookieConfig.class).autowire();
243+
String registrationId = this.clientRegistration.getRegistrationId();
244+
MockHttpSession session = login();
245+
String logoutToken = this.mvc.perform(get("/token/logout").session(session))
246+
.andExpect(status().isOk())
247+
.andReturn()
248+
.getResponse()
249+
.getContentAsString();
250+
this.mvc
251+
.perform(post(this.web.url("/logout/connect/back-channel/" + registrationId).toString())
252+
.param("logout_token", logoutToken))
253+
.andExpect(status().isOk());
254+
this.mvc.perform(get("/token/logout").session(session)).andExpect(status().isUnauthorized());
255+
}
256+
238257
@Test
239258
void logoutWhenRemoteLogoutFailsThenReportsPartialLogout() throws Exception {
240259
this.spring.register(WebServerConfig.class, OidcProviderConfig.class, WithBrokenLogoutConfig.class).autowire();
@@ -396,6 +415,51 @@ SecurityFilterChain filters(HttpSecurity http) throws Exception {
396415

397416
}
398417

418+
@Configuration
419+
@EnableWebSecurity
420+
@Import(RegistrationConfig.class)
421+
static class CookieConfig {
422+
423+
private final MockWebServer server = new MockWebServer();
424+
425+
@Bean
426+
@Order(1)
427+
SecurityFilterChain filters(HttpSecurity http) throws Exception {
428+
// @formatter:off
429+
http
430+
.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())
431+
.oauth2Login(Customizer.withDefaults())
432+
.oidcLogout((oidc) -> oidc
433+
.backChannel((backchannel) -> backchannel
434+
.sessionLogout((logout) -> logout.cookieName("SESSION"))
435+
)
436+
);
437+
// @formatter:on
438+
439+
return http.build();
440+
}
441+
442+
@Bean
443+
MockWebServer web(ObjectProvider<MockMvc> mvc) {
444+
MockMvcDispatcher dispatcher = new MockMvcDispatcher(mvc);
445+
dispatcher.setAssertion((rr) -> {
446+
String cookie = rr.getHeaders().get("Cookie");
447+
if (cookie == null) {
448+
return;
449+
}
450+
assertThat(cookie).contains("SESSION").doesNotContain("JSESSIONID");
451+
});
452+
this.server.setDispatcher(dispatcher);
453+
return this.server;
454+
}
455+
456+
@PreDestroy
457+
void shutdown() throws IOException {
458+
this.server.shutdown();
459+
}
460+
461+
}
462+
399463
@Configuration
400464
@EnableWebSecurity
401465
@Import(RegistrationConfig.class)
@@ -600,12 +664,15 @@ private static class MockMvcDispatcher extends Dispatcher {
600664

601665
private MockMvc mvc;
602666

667+
private Consumer<RecordedRequest> assertion = (rr) -> { };
668+
603669
MockMvcDispatcher(ObjectProvider<MockMvc> mvc) {
604670
this.mvcProvider = mvc;
605671
}
606672

607673
@Override
608674
public MockResponse dispatch(RecordedRequest request) throws InterruptedException {
675+
this.assertion.accept(request);
609676
this.mvc = this.mvcProvider.getObject();
610677
String method = request.getMethod();
611678
String path = request.getPath();
@@ -642,6 +709,10 @@ void registerSession(MockHttpSession session) {
642709
this.session.put(session.getId(), session);
643710
}
644711

712+
void setAssertion(Consumer<RecordedRequest> assertion) {
713+
this.assertion = assertion;
714+
}
715+
645716
private MockHttpSession session(RecordedRequest request) {
646717
String cookieHeaderValue = request.getHeader("Cookie");
647718
if (cookieHeaderValue == null) {
@@ -654,6 +725,10 @@ private MockHttpSession session(RecordedRequest request) {
654725
return this.session.computeIfAbsent(parts[1],
655726
(k) -> new MockHttpSession(new MockServletContext(), parts[1]));
656727
}
728+
if ("SESSION".equals(parts[0])) {
729+
return this.session.computeIfAbsent(parts[1],
730+
(k) -> new MockHttpSession(new MockServletContext(), parts[1]));
731+
}
657732
}
658733
return new MockHttpSession();
659734
}

0 commit comments

Comments
 (0)