Skip to content

Commit d0c7a9b

Browse files
committed
Polish gh-16551
1 parent f35d956 commit d0c7a9b

File tree

4 files changed

+36
-37
lines changed

4 files changed

+36
-37
lines changed

docs/modules/ROOT/pages/reactive/oauth2/login/logout.adoc

+1-1
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ If used, the application's base URL, such as `https://app.example.org`, replaces
126126
[NOTE]
127127
====
128128
By default, `OidcClientInitiatedServerLogoutSuccessHandler` redirects to the logout URL using a standard HTTP redirect with the `GET` method.
129-
To perform the logout using a `POST` request, set the redirect strategy to `ServerFormPostRedirectStrategy`, for example with `OidcClientInitiatedServerLogoutSuccessHandler.setRedirectStrategy(new ServerFormPostRedirectStrategy())`.
129+
To perform the logout using a `POST` request, set the redirect strategy to `FormPostServerRedirectStrategy`, for example with `OidcClientInitiatedServerLogoutSuccessHandler.setRedirectStrategy(new ServerFormPostRedirectStrategy())`.
130130
====
131131

132132
[[configure-provider-initiated-oidc-logout]]

oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/web/server/logout/OidcClientInitiatedServerLogoutSuccessHandlerTests.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ public void setRedirectStrategyWhenGivenNullThenThrowsException() {
229229
}
230230

231231
@Test
232-
public void logoutWhenCustomRedirectStrategySetThenCustomRedirectStrategyUse() {
232+
public void logoutWhenCustomRedirectStrategySetThenCustomRedirectStrategyUsed() {
233233
ServerRedirectStrategy redirectStrategy = mock(ServerRedirectStrategy.class);
234234
given(redirectStrategy.sendRedirect(any(), any())).willReturn(Mono.empty());
235235
OAuth2AuthenticationToken token = new OAuth2AuthenticationToken(TestOidcUsers.create(),

web/src/main/java/org/springframework/security/web/server/ServerFormPostRedirectStrategy.java renamed to web/src/main/java/org/springframework/security/web/server/FormPostServerRedirectStrategy.java

+29-30
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
import org.springframework.core.io.buffer.DataBuffer;
2828
import org.springframework.core.io.buffer.DataBufferFactory;
29+
import org.springframework.core.io.buffer.DataBufferUtils;
2930
import org.springframework.http.HttpStatus;
3031
import org.springframework.http.MediaType;
3132
import org.springframework.http.server.reactive.ServerHttpResponse;
@@ -41,15 +42,13 @@
4142
* data instead of query string data.
4243
*
4344
* @author Max Batischev
45+
* @author Steve Riesenberg
4446
* @since 6.5
4547
*/
46-
public final class ServerFormPostRedirectStrategy implements ServerRedirectStrategy {
48+
public final class FormPostServerRedirectStrategy implements ServerRedirectStrategy {
4749

4850
private static final String CONTENT_SECURITY_POLICY_HEADER = "Content-Security-Policy";
4951

50-
private static final StringKeyGenerator DEFAULT_NONCE_GENERATOR = new Base64StringKeyGenerator(
51-
Base64.getUrlEncoder().withoutPadding(), 96);
52-
5352
private static final String REDIRECT_PAGE_TEMPLATE = """
5453
<!DOCTYPE html>
5554
<html lang="en">
@@ -79,46 +78,46 @@ public final class ServerFormPostRedirectStrategy implements ServerRedirectStrat
7978
<input name="{{name}}" type="hidden" value="{{value}}" />
8079
""";
8180

81+
private static final StringKeyGenerator DEFAULT_NONCE_GENERATOR = new Base64StringKeyGenerator(
82+
Base64.getUrlEncoder().withoutPadding(), 96);
83+
8284
@Override
8385
public Mono<Void> sendRedirect(ServerWebExchange exchange, URI location) {
84-
String nonce = DEFAULT_NONCE_GENERATOR.generateKey();
85-
String policyDirective = "script-src 'nonce-%s'".formatted(nonce);
86-
87-
ServerHttpResponse response = exchange.getResponse();
88-
response.setStatusCode(HttpStatus.OK);
89-
response.getHeaders().setContentType(MediaType.TEXT_HTML);
90-
response.getHeaders().add(CONTENT_SECURITY_POLICY_HEADER, policyDirective);
91-
return response.writeWith(createBuffer(exchange, location, nonce));
92-
}
86+
final UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUri(location);
9387

94-
private Mono<DataBuffer> createBuffer(ServerWebExchange exchange, URI location, String nonce) {
95-
byte[] bytes = createPage(location, nonce);
96-
DataBufferFactory bufferFactory = exchange.getResponse().bufferFactory();
97-
return Mono.just(bufferFactory.wrap(bytes));
98-
}
99-
100-
private byte[] createPage(URI location, String nonce) {
101-
UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUri(location);
102-
103-
StringBuilder hiddenInputsHtmlBuilder = new StringBuilder();
88+
final StringBuilder hiddenInputsHtmlBuilder = new StringBuilder();
10489
for (final Map.Entry<String, List<String>> entry : uriComponentsBuilder.build().getQueryParams().entrySet()) {
10590
final String name = entry.getKey();
10691
for (final String value : entry.getValue()) {
10792
// @formatter:off
10893
final String hiddenInput = HIDDEN_INPUT_TEMPLATE
109-
.replace("{{name}}", HtmlUtils.htmlEscape(name))
110-
.replace("{{value}}", HtmlUtils.htmlEscape(value));
94+
.replace("{{name}}", HtmlUtils.htmlEscape(name))
95+
.replace("{{value}}", HtmlUtils.htmlEscape(value));
11196
// @formatter:on
11297
hiddenInputsHtmlBuilder.append(hiddenInput.trim());
11398
}
11499
}
100+
101+
// Create the script-src policy directive for the Content-Security-Policy header
102+
final String nonce = DEFAULT_NONCE_GENERATOR.generateKey();
103+
final String policyDirective = "script-src 'nonce-%s'".formatted(nonce);
104+
115105
// @formatter:off
116-
return REDIRECT_PAGE_TEMPLATE
117-
.replace("{{action}}", HtmlUtils.htmlEscape(uriComponentsBuilder.query(null).build().toUriString()))
118-
.replace("{{params}}", hiddenInputsHtmlBuilder.toString())
119-
.replace("{{nonce}}", HtmlUtils.htmlEscape(nonce))
120-
.getBytes(StandardCharsets.UTF_8);
106+
final String html = REDIRECT_PAGE_TEMPLATE
107+
// Clear the query string as we don't want that to be part of the form action URL
108+
.replace("{{action}}", HtmlUtils.htmlEscape(uriComponentsBuilder.query(null).build().toUriString()))
109+
.replace("{{params}}", hiddenInputsHtmlBuilder.toString())
110+
.replace("{{nonce}}", HtmlUtils.htmlEscape(nonce));
121111
// @formatter:on
112+
113+
final ServerHttpResponse response = exchange.getResponse();
114+
response.setStatusCode(HttpStatus.OK);
115+
response.getHeaders().setContentType(MediaType.TEXT_HTML);
116+
response.getHeaders().set(CONTENT_SECURITY_POLICY_HEADER, policyDirective);
117+
118+
final DataBufferFactory bufferFactory = response.bufferFactory();
119+
final DataBuffer buffer = bufferFactory.wrap(html.getBytes(StandardCharsets.UTF_8));
120+
return response.writeWith(Mono.just(buffer)).doOnError((error) -> DataBufferUtils.release(buffer));
122121
}
123122

124123
}

web/src/test/java/org/springframework/security/web/server/ServerFormPostRedirectStrategyTests.java renamed to web/src/test/java/org/springframework/security/web/server/FormPostServerRedirectStrategyTests.java

+5-5
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,15 @@
3030
import static org.assertj.core.api.Assertions.assertThat;
3131

3232
/**
33-
* Tests for {@link ServerFormPostRedirectStrategy}.
33+
* Tests for {@link FormPostServerRedirectStrategy}.
3434
*
3535
* @author Max Batischev
3636
*/
37-
public class ServerFormPostRedirectStrategyTests {
37+
public class FormPostServerRedirectStrategyTests {
3838

3939
private static final String POLICY_DIRECTIVE_PATTERN = "script-src 'nonce-(.+)'";
4040

41-
private final ServerRedirectStrategy redirectStrategy = new ServerFormPostRedirectStrategy();
41+
private final ServerRedirectStrategy redirectStrategy = new FormPostServerRedirectStrategy();
4242

4343
private final MockServerHttpRequest request = MockServerHttpRequest.get("https://localhost").build();
4444

@@ -89,7 +89,7 @@ public void redirectWhenLocationAbsoluteUriWithFragmentIsPresentThenRedirect() {
8989
}
9090

9191
@Test
92-
public void redirectWhenLocationAbsoluteUilWithQueryParamsIsPresentThenRedirect() {
92+
public void redirectWhenLocationAbsoluteUriWithQueryParamsIsPresentThenRedirect() {
9393
this.redirectStrategy
9494
.sendRedirect(this.webExchange, URI.create("https://example.com/path?param1=one&param2=two#fragment"))
9595
.block();
@@ -105,7 +105,7 @@ public void redirectWhenLocationAbsoluteUilWithQueryParamsIsPresentThenRedirect(
105105

106106
private ThrowingConsumer<MockServerHttpResponse> hasScriptSrcNonce() {
107107
return (response) -> {
108-
final String policyDirective = response.getHeaders().get("Content-Security-Policy").get(0);
108+
final String policyDirective = response.getHeaders().getFirst("Content-Security-Policy");
109109
assertThat(policyDirective).isNotEmpty();
110110
assertThat(policyDirective).matches(POLICY_DIRECTIVE_PATTERN);
111111

0 commit comments

Comments
 (0)