Make Internal Logout URI Configurable

Closes gh-14609
This commit is contained in:
Josh Cummings
2024-03-22 16:28:01 -06:00
parent e18ec48134
commit 44033cd8b9
8 changed files with 288 additions and 32 deletions

View File

@@ -35,4 +35,28 @@ public class OidcBackChannelLogoutHandlerTests {
assertThat(endpoint).isEqualTo("http://localhost:8090/logout");
}
@Test
public void computeLogoutEndpointWhenUsingBaseUrlTemplateThenServerName() {
OidcBackChannelLogoutHandler logoutHandler = new OidcBackChannelLogoutHandler();
logoutHandler.setLogoutUri("{baseUrl}/logout");
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/back-channel/logout");
request.setServerName("host.docker.internal");
request.setServerPort(8090);
String endpoint = logoutHandler.computeLogoutEndpoint(request);
assertThat(endpoint).isEqualTo("http://host.docker.internal:8090/logout");
}
// gh-14609
@Test
public void computeLogoutEndpointWhenLogoutUriThenUses() {
OidcBackChannelLogoutHandler logoutHandler = new OidcBackChannelLogoutHandler();
logoutHandler.setLogoutUri("http://localhost:8090/logout");
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/back-channel/logout");
request.setScheme("https");
request.setServerName("server-one.com");
request.setServerPort(80);
String endpoint = logoutHandler.computeLogoutEndpoint(request);
assertThat(endpoint).isEqualTo("http://localhost:8090/logout");
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -197,6 +197,25 @@ public class OidcLogoutConfigurerTests {
this.mvc.perform(get("/token/logout").session(three)).andExpect(status().isUnauthorized());
}
@Test
void logoutWhenRemoteLogoutUriThenUses() throws Exception {
this.spring.register(WebServerConfig.class, OidcProviderConfig.class, LogoutUriConfig.class).autowire();
String registrationId = this.clientRegistration.getRegistrationId();
MockHttpSession one = login();
String logoutToken = this.mvc.perform(get("/token/logout/all").session(one))
.andExpect(status().isOk())
.andReturn()
.getResponse()
.getContentAsString();
this.mvc
.perform(post(this.web.url("/logout/connect/back-channel/" + registrationId).toString())
.param("logout_token", logoutToken))
.andExpect(status().isBadRequest())
.andExpect(content().string(containsString("partial_logout")))
.andExpect(content().string(containsString("Connection refused")));
this.mvc.perform(get("/token/logout").session(one)).andExpect(status().isOk());
}
@Test
void logoutWhenRemoteLogoutFailsThenReportsPartialLogout() throws Exception {
this.spring.register(WebServerConfig.class, OidcProviderConfig.class, WithBrokenLogoutConfig.class).autowire();
@@ -312,6 +331,28 @@ public class OidcLogoutConfigurerTests {
}
@Configuration
@EnableWebSecurity
@Import(RegistrationConfig.class)
static class LogoutUriConfig {
@Bean
@Order(1)
SecurityFilterChain filters(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())
.oauth2Login(Customizer.withDefaults())
.oidcLogout((oidc) -> oidc
.backChannel((backchannel) -> backchannel.logoutUri("http://localhost/wrong"))
);
// @formatter:on
return http.build();
}
}
@Configuration
@EnableWebSecurity
@Import(RegistrationConfig.class)

View File

@@ -38,4 +38,25 @@ public class OidcBackChannelServerLogoutHandlerTests {
assertThat(endpoint).isEqualTo("https://localhost:8090/logout");
}
@Test
public void computeLogoutEndpointWhenUsingBaseUrlTemplateThenServerName() {
OidcBackChannelServerLogoutHandler logoutHandler = new OidcBackChannelServerLogoutHandler();
logoutHandler.setLogoutUri("{baseUrl}/logout");
MockServerHttpRequest request = MockServerHttpRequest
.get("http://host.docker.internal:8090/back-channel/logout")
.build();
String endpoint = logoutHandler.computeLogoutEndpoint(request);
assertThat(endpoint).isEqualTo("http://host.docker.internal:8090/logout");
}
// gh-14609
@Test
public void computeLogoutEndpointWhenLogoutUriThenUses() {
OidcBackChannelServerLogoutHandler logoutHandler = new OidcBackChannelServerLogoutHandler();
logoutHandler.setLogoutUri("http://localhost:8090/logout");
MockServerHttpRequest request = MockServerHttpRequest.get("https://server-one.com/back-channel/logout").build();
String endpoint = logoutHandler.computeLogoutEndpoint(request);
assertThat(endpoint).isEqualTo("http://localhost:8090/logout");
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -242,6 +242,32 @@ public class OidcLogoutSpecTests {
this.test.get().uri("/token/logout").cookie("SESSION", three).exchange().expectStatus().isUnauthorized();
}
@Test
void logoutWhenRemoteLogoutUriThenUses() {
this.spring.register(WebServerConfig.class, OidcProviderConfig.class, LogoutUriConfig.class).autowire();
String registrationId = this.clientRegistration.getRegistrationId();
String one = login();
String logoutToken = this.test.get()
.uri("/token/logout/all")
.cookie("SESSION", one)
.exchange()
.expectStatus()
.isOk()
.returnResult(String.class)
.getResponseBody()
.blockFirst();
this.test.post()
.uri(this.web.url("/logout/connect/back-channel/" + registrationId).toString())
.body(BodyInserters.fromFormData("logout_token", logoutToken))
.exchange()
.expectStatus()
.isBadRequest()
.expectBody(String.class)
.value(containsString("partial_logout"))
.value(containsString("Connection refused"));
this.test.get().uri("/token/logout").cookie("SESSION", one).exchange().expectStatus().isOk();
}
@Test
void logoutWhenRemoteLogoutFailsThenReportsPartialLogout() {
this.spring.register(WebServerConfig.class, OidcProviderConfig.class, WithBrokenLogoutConfig.class).autowire();
@@ -396,6 +422,28 @@ public class OidcLogoutSpecTests {
}
@Configuration
@EnableWebFluxSecurity
@Import(RegistrationConfig.class)
static class LogoutUriConfig {
@Bean
@Order(1)
SecurityWebFilterChain filters(ServerHttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeExchange((authorize) -> authorize.anyExchange().authenticated())
.oauth2Login(Customizer.withDefaults())
.oidcLogout((oidc) -> oidc
.backChannel((backchannel) -> backchannel.logoutUri("http://localhost/wrong"))
);
// @formatter:on
return http.build();
}
}
@Configuration
@EnableWebFluxSecurity
@Import(RegistrationConfig.class)