Skip to content

Remove GET request support from Saml2AuthenticationTokenConverter #17108

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions docs/modules/ROOT/pages/migration/servlet/oauth2.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,42 @@ fun jwtDecoder(): JwtDecoder {
======
<1> - `validateTypes` now defaults to `false`
<2> - `JwtTypeValidator#jwt` is added by all `createDefaultXXX` methods

== Do Not Process `<saml2:Response>` GET Requests with `Saml2AuthenticationTokenConverter`

Spring Security does not support processing `<saml2:Response>` payloads over GET as this is not supported by the SAML 2.0 spec.

To better comply with this, `Saml2AuthenticationTokenConverter` will not process GET requests by default as of Spring Security 8.
To prepare for this, the property `shouldConvertGetRequests` is available.
To use it, publish your own `Saml2AuthenticationTokenConverter` like so:

[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
Saml2AuthenticationTokenConverter authenticationConverter(RelyingPartyRegistrationRepository registrations) {
Saml2AuhenticationTokenConverter authenticationConverter = new Saml2AuthenticationTokenConverter(
new DefaultRelyingPartyRegistrationResolver(registrations));
authenticationConverter.setShouldConvertGetRequests(false);
return authenticationConverter;
}
----

Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
fun authenticationConverter(val registrations: RelyingPartyRegistrationRepository): Saml2AuthenticationTokenConverter {
val authenticationConverter = new Saml2AuthenticationTokenConverter(
DefaultRelyingPartyRegistrationResolver(registrations))
authenticationConverter.setShouldConvertGetRequests(false)
return authenticationConverter
}
----
======

If you must continue using `Saml2AuthenticationTokenConverter` to process GET requests, you can call `setShouldConvertGetRequests` to `true.`
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2025 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.
Expand Down Expand Up @@ -43,6 +43,8 @@ public final class Saml2AuthenticationTokenConverter implements AuthenticationCo

private Saml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> authenticationRequestRepository;

private boolean shouldConvertGetRequests = true;

/**
* Constructs a {@link Saml2AuthenticationTokenConverter} given a strategy for
* resolving {@link RelyingPartyRegistration}s
Expand Down Expand Up @@ -86,16 +88,27 @@ public void setAuthenticationRequestRepository(
this.authenticationRequestRepository = authenticationRequestRepository;
}

/**
* Use the given {@code shouldConvertGetRequests} to convert {@code GET} requests.
* Default is {@code true}.
* @param shouldConvertGetRequests the {@code shouldConvertGetRequests} to use
* @since 7.0
*/
public void setShouldConvertGetRequests(boolean shouldConvertGetRequests) {
this.shouldConvertGetRequests = shouldConvertGetRequests;
}

private String decode(HttpServletRequest request) {
String encoded = request.getParameter(Saml2ParameterNames.SAML_RESPONSE);
if (encoded == null) {
return null;
}
boolean isGet = HttpMethod.GET.matches(request.getMethod());
if (!this.shouldConvertGetRequests && isGet) {
return null;
}
try {
return Saml2Utils.withEncoded(encoded)
.requireBase64(true)
.inflate(HttpMethod.GET.matches(request.getMethod()))
.decode();
return Saml2Utils.withEncoded(encoded).requireBase64(true).inflate(isGet).decode();
}
catch (Exception ex) {
throw new Saml2AuthenticationException(new Saml2Error(Saml2ErrorCodes.INVALID_RESPONSE, ex.getMessage()),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2025 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.
Expand Down Expand Up @@ -230,6 +230,21 @@ public void setAuthenticationRequestRepositoryWhenNullThenIllegalArgument() {
.isThrownBy(() -> converter.setAuthenticationRequestRepository(null));
}

@Test
public void shouldNotConvertGetRequests() {
Saml2AuthenticationTokenConverter converter = new Saml2AuthenticationTokenConverter(
this.relyingPartyRegistrationResolver);
converter.setShouldConvertGetRequests(false);
given(this.relyingPartyRegistrationResolver.resolve(any(HttpServletRequest.class), any()))
.willReturn(this.relyingPartyRegistration);
MockHttpServletRequest request = new MockHttpServletRequest();
request.setMethod("GET");
request.setParameter(Saml2ParameterNames.SAML_RESPONSE,
Saml2Utils.samlEncode("response".getBytes(StandardCharsets.UTF_8)));
Saml2AuthenticationToken token = converter.convert(request);
assertThat(token).isNull();
}

private void validateSsoCircleXml(String xml) {
assertThat(xml).contains("InResponseTo=\"ARQ9a73ead-7dcf-45a8-89eb-26f3c9900c36\"")
.contains(" ID=\"s246d157446618e90e43fb79bdd4d9e9e19cf2c7c4\"")
Expand Down