Skip to content

Commit 2702a64

Browse files
committed
Use Localhost for Internal Logout Endpoint
Closes gh-14553
1 parent 50429cf commit 2702a64

File tree

4 files changed

+101
-7
lines changed

4 files changed

+101
-7
lines changed

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

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -106,13 +106,18 @@ private void eachLogout(HttpServletRequest request, OidcSessionInformation sessi
106106
for (Map.Entry<String, String> credential : session.getAuthorities().entrySet()) {
107107
headers.add(credential.getKey(), credential.getValue());
108108
}
109+
String logout = computeLogoutEndpoint(request);
110+
HttpEntity<?> entity = new HttpEntity<>(null, headers);
111+
this.restOperations.postForEntity(logout, entity, Object.class);
112+
}
113+
114+
String computeLogoutEndpoint(HttpServletRequest request) {
109115
String url = request.getRequestURL().toString();
110-
String logout = UriComponentsBuilder.fromHttpUrl(url)
116+
return UriComponentsBuilder.fromHttpUrl(url)
117+
.host("localhost")
111118
.replacePath(this.logoutEndpointName)
112119
.build()
113120
.toUriString();
114-
HttpEntity<?> entity = new HttpEntity<>(null, headers);
115-
this.restOperations.postForEntity(logout, entity, Object.class);
116121
}
117122

118123
private OAuth2Error oauth2Error(Collection<String> errors) {

config/src/main/java/org/springframework/security/config/web/server/OidcBackChannelServerLogoutHandler.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -108,12 +108,17 @@ private Mono<ResponseEntity<Void>> eachLogout(WebFilterExchange exchange, OidcSe
108108
for (Map.Entry<String, String> credential : session.getAuthorities().entrySet()) {
109109
headers.add(credential.getKey(), credential.getValue());
110110
}
111+
String logout = computeLogoutEndpoint(exchange);
112+
return this.web.post().uri(logout).headers((h) -> h.putAll(headers)).retrieve().toBodilessEntity();
113+
}
114+
115+
String computeLogoutEndpoint(WebFilterExchange exchange) {
111116
String url = exchange.getExchange().getRequest().getURI().toString();
112-
String logout = UriComponentsBuilder.fromHttpUrl(url)
117+
return UriComponentsBuilder.fromHttpUrl(url)
118+
.host("localhost")
113119
.replacePath(this.logoutEndpointName)
114120
.build()
115121
.toUriString();
116-
return this.web.post().uri(logout).headers((h) -> h.putAll(headers)).retrieve().toBodilessEntity();
117122
}
118123

119124
private OAuth2Error oauth2Error(Collection<?> errors) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright 2002-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.config.annotation.web.configurers.oauth2.client;
18+
19+
import org.junit.jupiter.api.Test;
20+
21+
import org.springframework.mock.web.MockHttpServletRequest;
22+
23+
import static org.assertj.core.api.Assertions.assertThat;
24+
25+
public class OidcBackChannelLogoutHandlerTests {
26+
27+
// gh-14553
28+
@Test
29+
public void computeLogoutEndpointWhenDifferentHostnameThenLocalhost() {
30+
OidcBackChannelLogoutHandler logoutHandler = new OidcBackChannelLogoutHandler();
31+
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/back-channel/logout");
32+
request.setRemoteHost("host.docker.internal");
33+
request.setServerPort(8090);
34+
String endpoint = logoutHandler.computeLogoutEndpoint(request);
35+
assertThat(endpoint).isEqualTo("http://localhost:8090/logout");
36+
}
37+
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright 2002-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.config.web.server;
18+
19+
import org.junit.jupiter.api.Test;
20+
import reactor.core.publisher.Mono;
21+
22+
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
23+
import org.springframework.mock.web.server.MockServerWebExchange;
24+
import org.springframework.security.web.server.WebFilterExchange;
25+
import org.springframework.web.server.ServerWebExchange;
26+
27+
import static org.assertj.core.api.Assertions.assertThat;
28+
29+
/**
30+
* Tests for {@link OidcBackChannelServerLogoutHandler}
31+
*/
32+
public class OidcBackChannelServerLogoutHandlerTests {
33+
34+
// gh-14553
35+
@Test
36+
public void computeLogoutEndpointWhenDifferentHostnameThenLocalhost() {
37+
OidcBackChannelServerLogoutHandler logoutHandler = new OidcBackChannelServerLogoutHandler();
38+
MockServerHttpRequest request = MockServerHttpRequest
39+
.get("https://host.docker.internal:8090/back-channel/logout")
40+
.build();
41+
ServerWebExchange exchange = new MockServerWebExchange.Builder(request).build();
42+
String endpoint = logoutHandler.computeLogoutEndpoint(new WebFilterExchange(exchange, (ex) -> Mono.empty()));
43+
assertThat(endpoint).isEqualTo("https://localhost:8090/logout");
44+
}
45+
46+
}

0 commit comments

Comments
 (0)