diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml4AuthenticationProvider.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml4AuthenticationProvider.java index a9010b5a58..d523999d60 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml4AuthenticationProvider.java +++ b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml4AuthenticationProvider.java @@ -23,6 +23,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.function.Consumer; @@ -674,7 +675,7 @@ public final class OpenSaml4AuthenticationProvider implements AuthenticationProv attributeMap.addAll(attribute.getName(), attributeValues); } } - return attributeMap; + return new LinkedHashMap<>(attributeMap); // gh-11785 } private static List getSessionIndexes(Assertion assertion) { diff --git a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml4AuthenticationProviderTests.java b/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml4AuthenticationProviderTests.java index e4023a7be4..eb4de8e7e6 100644 --- a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml4AuthenticationProviderTests.java +++ b/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml4AuthenticationProviderTests.java @@ -32,6 +32,7 @@ import java.util.function.Consumer; import javax.xml.namespace.QName; +import com.fasterxml.jackson.databind.ObjectMapper; import net.shibboleth.utilities.java.support.xml.SerializeSupport; import org.junit.jupiter.api.Test; import org.opensaml.core.xml.XMLObject; @@ -68,6 +69,7 @@ import org.w3c.dom.Element; import org.springframework.core.convert.converter.Converter; import org.springframework.security.core.Authentication; +import org.springframework.security.jackson2.SecurityJackson2Modules; import org.springframework.security.saml2.Saml2Exception; import org.springframework.security.saml2.core.Saml2Error; import org.springframework.security.saml2.core.Saml2ErrorCodes; @@ -349,6 +351,23 @@ public class OpenSaml4AuthenticationProviderTests { assertThat(principal.getSessionIndexes()).contains("session-index"); } + // gh-11785 + @Test + public void deserializeWhenAssertionContainsAttributesThenWorks() throws Exception { + ObjectMapper mapper = new ObjectMapper(); + ClassLoader loader = getClass().getClassLoader(); + mapper.registerModules(SecurityJackson2Modules.getModules(loader)); + Response response = response(); + Assertion assertion = assertion(); + List attributes = TestOpenSamlObjects.attributeStatements(); + assertion.getAttributeStatements().addAll(attributes); + response.getAssertions().add(signed(assertion)); + Saml2AuthenticationToken token = token(response, verifying(registration())); + Authentication authentication = this.provider.authenticate(token); + String result = mapper.writeValueAsString(authentication); + mapper.readValue(result, Authentication.class); + } + @Test public void authenticateWhenAssertionContainsCustomAttributesThenItSucceeds() { Response response = response();