[java-plugin-shibd-saml] 01/02: Fix discontinuity in encoding of opaque session data.

Codeberg noreply at shibboleth.net
Mon Jun 1 16:53:50 UTC 2026


This is an automated email from the git hooks/post-receive script.

codeberg pushed a commit to branch main
in repository java-plugin-shibd-saml.

View the commit online:
https://codeberg.org/Shibboleth/java-plugin-shibd-saml/commit/126e9c21b50f3aa7a66dab9d4f91c269089d7cc5

commit 126e9c21b50f3aa7a66dab9d4f91c269089d7cc5
Author: Scott Cantor <scott at restingparrotsoftware.com>
AuthorDate: Mon Jun 1 12:00:25 2026 -0400

    Fix discontinuity in encoding of opaque session data.
---
 .../flows/saml2/SAML2LogoutConsumerFlowTest.java   | 15 +++++----
 .../saml2/profile/impl/ProcessLogoutRequest.java   | 20 +++++++-----
 .../profile/impl/ProcessLogoutRequestTest.java     | 36 +++++++++++++---------
 3 files changed, 44 insertions(+), 27 deletions(-)

diff --git a/sp-saml-conf-impl/src/test/java/net/shibboleth/sp/saml/flows/saml2/SAML2LogoutConsumerFlowTest.java b/sp-saml-conf-impl/src/test/java/net/shibboleth/sp/saml/flows/saml2/SAML2LogoutConsumerFlowTest.java
index 6259564..d642b82 100644
--- a/sp-saml-conf-impl/src/test/java/net/shibboleth/sp/saml/flows/saml2/SAML2LogoutConsumerFlowTest.java
+++ b/sp-saml-conf-impl/src/test/java/net/shibboleth/sp/saml/flows/saml2/SAML2LogoutConsumerFlowTest.java
@@ -285,9 +285,10 @@ public class SAML2LogoutConsumerFlowTest extends AbstractSPFlowTest {
         final LogoutRequest request = buildLogoutRequest(ISSUER);
         sign(request);
         final DDF input = buildRemotedPOSTMessage(request, RELAY_STATE, null);
+        final String opaque = "<NameID xmlns='urn:oasis:names:tc:SAML:2.0:assertion' SPProvidedID='" + ISSUER
+                + "'>jdoe at example.org</NameID>";
         input.addmember(ConsumerConstants.SESSION_OPAQUE).addmember(PrepareAgentResponse.NAMEID_PARAM).string(
-                "<NameID xmlns='urn:oasis:names:tc:SAML:2.0:assertion'"
-                        + "' SPProvidedID='" + ISSUER + "'>jdoe at example.org</NameID>");
+                Base64Support.encodeURLSafe(opaque.getBytes(StandardCharsets.UTF_8)));
         setApplicationRequest(APPLICATION_ID, input);
         
         validateLogoutRequestResult(false);
@@ -302,9 +303,10 @@ public class SAML2LogoutConsumerFlowTest extends AbstractSPFlowTest {
     public void testRequestUnsigned() throws Exception {
         final LogoutRequest request = buildLogoutRequest(ISSUER);
         final DDF input = buildRemotedPOSTMessage(request, RELAY_STATE, null);
+        final String opaque = "<NameID xmlns='urn:oasis:names:tc:SAML:2.0:assertion' Format='"
+                + NameIDType.EMAIL + "' SPProvidedID='" + ISSUER + "'>jdoe at example.org</NameID>";
         input.addmember(ConsumerConstants.SESSION_OPAQUE).addmember(PrepareAgentResponse.NAMEID_PARAM).string(
-                "<NameID xmlns='urn:oasis:names:tc:SAML:2.0:assertion' Format='"
-                        + NameIDType.EMAIL + "' SPProvidedID='" + ISSUER + "'>jdoe at example.org</NameID>");
+                Base64Support.encodeURLSafe(opaque.getBytes(StandardCharsets.UTF_8)));
         setApplicationRequest(APPLICATION_ID, input);
         
         validateError(EventIds.INVALID_MESSAGE);
@@ -320,9 +322,10 @@ public class SAML2LogoutConsumerFlowTest extends AbstractSPFlowTest {
         final LogoutRequest request = buildLogoutRequest(ISSUER);
         sign(request);
         final DDF input = buildRemotedPOSTMessage(request, RELAY_STATE, null);
+        final String opaque = "<NameID xmlns='urn:oasis:names:tc:SAML:2.0:assertion' Format='"
+                + NameIDType.EMAIL + "' SPProvidedID='" + ISSUER + "'>jdoe at example.org</NameID>";
         input.addmember(ConsumerConstants.SESSION_OPAQUE).addmember(PrepareAgentResponse.NAMEID_PARAM).string(
-                "<NameID xmlns='urn:oasis:names:tc:SAML:2.0:assertion' Format='"
-                        + NameIDType.EMAIL + "' SPProvidedID='" + ISSUER + "'>jdoe at example.org</NameID>");
+                Base64Support.encodeURLSafe(opaque.getBytes(StandardCharsets.UTF_8)));
         setApplicationRequest(APPLICATION_ID, input);
         
         validateLogoutRequestResult(true);
diff --git a/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/profile/impl/ProcessLogoutRequest.java b/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/profile/impl/ProcessLogoutRequest.java
index 64418df..f8a97c3 100644
--- a/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/profile/impl/ProcessLogoutRequest.java
+++ b/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/profile/impl/ProcessLogoutRequest.java
@@ -14,8 +14,9 @@
 
 package net.shibboleth.sp.saml.saml2.profile.impl;
 
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
 import java.io.IOException;
-import java.io.StringReader;
 import java.time.Instant;
 import java.util.Collection;
 import java.util.HashSet;
@@ -57,6 +58,8 @@ import net.shibboleth.saml.saml2.profile.config.navigate.QualifiedNameIDFormatsL
 import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
 import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
 import net.shibboleth.shared.annotation.constraint.NotEmpty;
+import net.shibboleth.shared.codec.Base64Support;
+import net.shibboleth.shared.codec.DecodingException;
 import net.shibboleth.shared.collection.CollectionSupport;
 import net.shibboleth.shared.component.ComponentInitializationException;
 import net.shibboleth.shared.logic.Constraint;
@@ -256,13 +259,16 @@ public class ProcessLogoutRequest extends AbstractApplicationAction {
         // Decode NameID from session data from Agent and unpack the buried information.
         NameID sessionNameID = null;
         try {
-            final XMLObject xmlObject = XMLObjectSupport.unmarshallFromReader(parserPool, new StringReader(pickled));
-            if (xmlObject instanceof NameID n) {
-                sessionNameID = n;
-            } else {
-                throw new XMLParserException("Decoded object was of unexpected type.");
+            final byte[] bytes = Base64Support.decodeURLSafe(pickled);
+            try (final InputStream source = new ByteArrayInputStream(bytes)) {
+                final XMLObject xmlObject = XMLObjectSupport.unmarshallFromInputStream(parserPool, source);
+                if (xmlObject instanceof NameID n) {
+                    sessionNameID = n;
+                } else {
+                    throw new XMLParserException("Decoded object was of unexpected type.");
+                }
             }
-        } catch (final XMLParserException | UnmarshallingException e) {
+        } catch (final DecodingException | IOException | XMLParserException | UnmarshallingException e) {
             log.warn("{} Failed to decode session information", getLogPrefix(), e);
         }
 
diff --git a/sp-saml-impl/src/test/java/net/shibboleth/sp/saml/saml2/profile/impl/ProcessLogoutRequestTest.java b/sp-saml-impl/src/test/java/net/shibboleth/sp/saml/saml2/profile/impl/ProcessLogoutRequestTest.java
index 19c95f5..743d30a 100644
--- a/sp-saml-impl/src/test/java/net/shibboleth/sp/saml/saml2/profile/impl/ProcessLogoutRequestTest.java
+++ b/sp-saml-impl/src/test/java/net/shibboleth/sp/saml/saml2/profile/impl/ProcessLogoutRequestTest.java
@@ -16,6 +16,7 @@ package net.shibboleth.sp.saml.saml2.profile.impl;
 
 
 import java.io.IOException;
+import java.nio.charset.StandardCharsets;
 import java.time.Instant;
 
 import javax.annotation.Nonnull;
@@ -48,6 +49,8 @@ import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
 
 import net.shibboleth.idp.profile.testing.ActionTestingSupport;
 import net.shibboleth.profile.context.RelyingPartyContext;
+import net.shibboleth.shared.codec.Base64Support;
+import net.shibboleth.shared.codec.EncodingException;
 import net.shibboleth.shared.component.ComponentInitializationException;
 import net.shibboleth.shared.resource.Resource;
 import net.shibboleth.shared.security.DataSealer;
@@ -248,10 +251,11 @@ public class ProcessLogoutRequestTest extends BaseApplicationActionTest {
     }
 
     @Test
-    public void testIncompleteSessionData() throws IOException {
+    public void testIncompleteSessionData() throws IOException, EncodingException {
         final DDF input = new DDF(null).structure();
+        final String opaque = "<NameID xmlns='urn:oasis:names:tc:SAML:2.0:assertion' Format='bar'>foo</NameID>"; 
         input.addmember(ConsumerConstants.SESSION_OPAQUE).addmember(PrepareAgentResponse.NAMEID_PARAM).string(
-                "<NameID xmlns='urn:oasis:names:tc:SAML:2.0:assertion' Format='bar'>foo</NameID>");
+                Base64Support.encodeURLSafe(opaque.getBytes(StandardCharsets.UTF_8)));
         arc.setInput(input);
 
         final Event event = action.execute(src);
@@ -264,11 +268,12 @@ public class ProcessLogoutRequestTest extends BaseApplicationActionTest {
     }
 
     @Test
-    public void testMatchNoIndex() throws IOException {
+    public void testMatchNoIndex() throws IOException, EncodingException {
         final DDF input = new DDF(null).structure();
+        final String opaque = "<NameID xmlns='urn:oasis:names:tc:SAML:2.0:assertion' SPProvidedID='"
+                + ActionTestingSupport.INBOUND_MSG_ISSUER + "'>jdoe</NameID>"; 
         input.addmember(ConsumerConstants.SESSION_OPAQUE).addmember(PrepareAgentResponse.NAMEID_PARAM).string(
-                "<NameID xmlns='urn:oasis:names:tc:SAML:2.0:assertion' SPProvidedID='"
-                        + ActionTestingSupport.INBOUND_MSG_ISSUER + "'>jdoe</NameID>");
+                Base64Support.encodeURLSafe(opaque.getBytes(StandardCharsets.UTF_8)));
         arc.setInput(input);
 
         final Event event = action.execute(src);
@@ -281,11 +286,12 @@ public class ProcessLogoutRequestTest extends BaseApplicationActionTest {
     }
 
     @Test
-    public void testNoMatchWithIndex() throws IOException {
+    public void testNoMatchWithIndex() throws IOException, EncodingException {
         final DDF input = new DDF(null).structure();
+        final String opaque = "<NameID xmlns='urn:oasis:names:tc:SAML:2.0:assertion' SPProvidedID='"
+                + ActionTestingSupport.INBOUND_MSG_ISSUER + "!!12345'>jdoe</NameID>"; 
         input.addmember(ConsumerConstants.SESSION_OPAQUE).addmember(PrepareAgentResponse.NAMEID_PARAM).string(
-                "<NameID xmlns='urn:oasis:names:tc:SAML:2.0:assertion' SPProvidedID='"
-                        + ActionTestingSupport.INBOUND_MSG_ISSUER + "!!12345'>jdoe</NameID>");
+                Base64Support.encodeURLSafe(opaque.getBytes(StandardCharsets.UTF_8)));
         arc.setInput(input);
 
         final SAMLObjectBuilder<SessionIndex> indexBuilder = (SAMLObjectBuilder<SessionIndex>)
@@ -309,11 +315,12 @@ public class ProcessLogoutRequestTest extends BaseApplicationActionTest {
     }
 
     @Test
-    public void testMatchWithIndex() throws IOException {
+    public void testMatchWithIndex() throws IOException, EncodingException {
         final DDF input = new DDF(null).structure();
+        final String opaque = "<NameID xmlns='urn:oasis:names:tc:SAML:2.0:assertion' SPProvidedID='"
+                + ActionTestingSupport.INBOUND_MSG_ISSUER + "!!12345'>jdoe</NameID>"; 
         input.addmember(ConsumerConstants.SESSION_OPAQUE).addmember(PrepareAgentResponse.NAMEID_PARAM).string(
-                "<NameID xmlns='urn:oasis:names:tc:SAML:2.0:assertion' SPProvidedID='"
-                        + ActionTestingSupport.INBOUND_MSG_ISSUER + "!!12345'>jdoe</NameID>");
+                Base64Support.encodeURLSafe(opaque.getBytes(StandardCharsets.UTF_8)));
         arc.setInput(input);
 
         final SAMLObjectBuilder<SessionIndex> indexBuilder = (SAMLObjectBuilder<SessionIndex>)
@@ -333,11 +340,12 @@ public class ProcessLogoutRequestTest extends BaseApplicationActionTest {
     }
     
     @Test
-    public void testMatchAsynch() throws IOException {
+    public void testMatchAsynch() throws IOException, EncodingException {
         final DDF input = new DDF(null).structure();
+        final String opaque = "<NameID xmlns='urn:oasis:names:tc:SAML:2.0:assertion' SPProvidedID='"
+                + ActionTestingSupport.INBOUND_MSG_ISSUER + "'>jdoe</NameID>"; 
         input.addmember(ConsumerConstants.SESSION_OPAQUE).addmember(PrepareAgentResponse.NAMEID_PARAM).string(
-                "<NameID xmlns='urn:oasis:names:tc:SAML:2.0:assertion' SPProvidedID='"
-                        + ActionTestingSupport.INBOUND_MSG_ISSUER + "'>jdoe</NameID>");
+                Base64Support.encodeURLSafe(opaque.getBytes(StandardCharsets.UTF_8)));
         arc.setInput(input);
 
         final SAMLObjectBuilder<Extensions> extsBuilder = (SAMLObjectBuilder<Extensions>)

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list