diff --git a/docs/modules/ROOT/pages/migration/servlet/oauth2.adoc b/docs/modules/ROOT/pages/migration/servlet/oauth2.adoc index 3caeec4133b..e80e0a916e3 100644 --- a/docs/modules/ROOT/pages/migration/servlet/oauth2.adoc +++ b/docs/modules/ROOT/pages/migration/servlet/oauth2.adoc @@ -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 `` GET Requests with `Saml2AuthenticationTokenConverter` + +Spring Security does not support processing `` 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.` diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/Saml2AuthenticationTokenConverter.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/Saml2AuthenticationTokenConverter.java index 8acc64bf97c..73872bbe033 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/Saml2AuthenticationTokenConverter.java +++ b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/Saml2AuthenticationTokenConverter.java @@ -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. @@ -43,6 +43,8 @@ public final class Saml2AuthenticationTokenConverter implements AuthenticationCo private Saml2AuthenticationRequestRepository authenticationRequestRepository; + private boolean shouldConvertGetRequests = true; + /** * Constructs a {@link Saml2AuthenticationTokenConverter} given a strategy for * resolving {@link RelyingPartyRegistration}s @@ -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()), diff --git a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/web/Saml2AuthenticationTokenConverterTests.java b/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/web/Saml2AuthenticationTokenConverterTests.java index f54788a4ea7..b023d38cb67 100644 --- a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/web/Saml2AuthenticationTokenConverterTests.java +++ b/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/web/Saml2AuthenticationTokenConverterTests.java @@ -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. @@ -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\"")