[java-plugin-shibd-oidc] branch main updated: Improve tests

Codeberg noreply at shibboleth.net
Mon Apr 13 12:57:11 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-oidc.

View the commit online:
https://codeberg.org/Shibboleth/java-plugin-shibd-oidc/commit/f30ea30a4ebf93995239296dcfd1f3e94f14eaef

The following commit(s) were added to refs/heads/main by this push:
     new f30ea30  Improve tests
f30ea30 is described below

commit f30ea30a4ebf93995239296dcfd1f3e94f14eaef
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Mon Apr 13 13:56:58 2026 +0100

    Improve tests
---
 .../sp/oidc/flows/OIDCTokenConsumerFlowTest.java   | 113 +++++++--------------
 .../shibboleth/sp/oidc/flows/TestConstants.java    |  12 ++-
 2 files changed, 43 insertions(+), 82 deletions(-)

diff --git a/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/OIDCTokenConsumerFlowTest.java b/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/OIDCTokenConsumerFlowTest.java
index 8812cfb..f0414d6 100644
--- a/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/OIDCTokenConsumerFlowTest.java
+++ b/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/OIDCTokenConsumerFlowTest.java
@@ -17,7 +17,6 @@ package net.shibboleth.sp.oidc.flows;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.fail;
 
-import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.net.URI;
 import java.net.URISyntaxException;
@@ -29,6 +28,7 @@ import java.util.Date;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
@@ -41,13 +41,8 @@ import org.apache.hc.core5.http.io.HttpClientResponseHandler;
 import org.apache.hc.core5.http.io.entity.StringEntity;
 import org.apache.hc.core5.http.protocol.HttpContext;
 import org.mockito.Mockito;
-import org.opensaml.core.xml.XMLObject;
-import org.opensaml.core.xml.io.UnmarshallingException;
-import org.opensaml.core.xml.util.XMLObjectSupport;
 import org.opensaml.profile.action.EventIds;
 import org.opensaml.profile.context.ProfileRequestContext;
-import org.opensaml.saml.saml2.core.NameID;
-import org.opensaml.saml.saml2.core.NameIDType;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.context.ApplicationContext;
@@ -88,11 +83,8 @@ import net.minidev.json.JSONObject;
 import net.shibboleth.idp.spring.IdPPropertiesApplicationContextInitializer;
 import net.shibboleth.idp.test.PreferFileSystemApplicationContextInitializer;
 import net.shibboleth.oidc.security.credential.JWKCredential;
-import net.shibboleth.shared.codec.Base64Support;
-import net.shibboleth.shared.codec.DecodingException;
 import net.shibboleth.shared.collection.CollectionSupport;
 import net.shibboleth.shared.logic.Constraint;
-import net.shibboleth.shared.xml.XMLParserException;
 import net.shibboleth.sp.context.AgentRequestContext;
 import net.shibboleth.sp.ddf.DDF;
 import net.shibboleth.sp.flows.AbstractSPFlowTest;
@@ -737,7 +729,9 @@ public class OIDCTokenConsumerFlowTest extends AbstractSPFlowTest {
         final FlowExecutionResult result = flowExecutor.launchExecution(TestConstants.FLOW_ID, null, externalContext);
         assertFlowExecutionResult(result, TestConstants.FLOW_ID);
         assertFlowExecutionOutcome(result.getOutcome());
-        assertOutputMessageEvent(result, EventIds.MESSAGE_PROC_ERROR);
+        final DDF output = assertOutputMessageEvent(result, EventIds.MESSAGE_PROC_ERROR);
+        System.out.println("DDF: "+output.toString());        
+        validateCookiesAreUnset(output);
     }
   
     
@@ -920,7 +914,34 @@ public class OIDCTokenConsumerFlowTest extends AbstractSPFlowTest {
             .thenReturn(classicHttpResponse);
     }
     
-
+    /**
+     * Check that the output message contains an instruction to unset cookies using Max-Age=0, and specifically the 
+     * correlation cookie. 
+     * 
+     * @param output the DDF output
+     */
+    private void validateCookiesAreUnset(@Nullable final DDF output) {
+        if (output == null) {
+            fail("DDF output can not be null");
+        }
+        // Check cookies are unset
+        final DDF headers = output.getmember("http.headers");
+        // Check that any Set-Cookie is an unset, we do not set new cookies in the consumer flow
+        final AtomicBoolean correlationCookieUnset = new AtomicBoolean(false);
+        if (headers.islist()) {
+            headers.forEach(header -> {
+                if (header.isstring() && "Set-Cookie".equals(header.name())){
+                    Assert.assertTrue(header.string().contains("Max-Age=0"),"Cookies must be unset");
+                }
+                // Specific check for the correlation cookie unset, which is required
+                if (header.string().contains(TestConstants.CORRELATION_COOKIE_PREFIX)) {
+                    Assert.assertTrue(header.string().contains("Max-Age=0"),"Correlation cookies must be unset");
+                    correlationCookieUnset.set(true);
+                }
+            });
+        }
+        Assert.assertTrue(correlationCookieUnset.get(),"Correlation cookies must be unset");
+    }
     
     /**
      * Decode an encoded response and run assertion checks against it.
@@ -968,6 +989,8 @@ public class OIDCTokenConsumerFlowTest extends AbstractSPFlowTest {
             }
         }
         Assert.assertTrue(mutableIds.isEmpty());
+        
+        validateCookiesAreUnset(output);
         //TODO ADD BACK
         
 //        try {
@@ -997,74 +1020,6 @@ public class OIDCTokenConsumerFlowTest extends AbstractSPFlowTest {
         
     }
 
-    /**
-     * Decode an encoded response and run sanity checks against it.
-     * 
-     * @param result flow execution result
-     * @param attributeIds set of attribute IDs to check for
-     * @param resource resource URL used in final redirect
-     * @param sessionIndex SessionIndex from assertion
-     * 
-     * @return the output object
-     * 
-     * TODO from the SAML variant. 
-     */
-    @Nonnull private DDF validateOutputMessage(@Nonnull final FlowExecutionResult result,
-            @Nullable final Set<String> attributeIds, @Nullable final String resource, @Nullable final String sessionIndex) {
-        final ProfileRequestContext prc = retrieveProfileRequestContext(result);
-        assert prc != null;
-        final AgentRequestContext arc = prc.ensureSubcontext(AgentRequestContext.class);
-        final DDF output = arc.getOutput();
-
-        assert output != null;
-        Assert.assertTrue(output.isstruct());
-        final DDF http = output.getmember(RemotedHttpServletRequest.STRUCTURE_NAME);
-        Assert.assertTrue(http.isstruct());
-        
-        final byte[] redirect = http.getmember(RemotedHttpServletResponse.REDIRECT).unsafe_string();
-        Assert.assertEquals(resource != null ? resource.getBytes(StandardCharsets.UTF_8) : null,  redirect);
-        
-        Assert.assertTrue(output.getmember(ConsumerConstants.VALIDATION_ERRORS).isnull());
-        
-        final Set<String> mutableIds = new HashSet<>(attributeIds);
-        for (final DDF attr : output.getmember(ConsumerConstants.SESSION_ATTRIBUTES).asList()) {
-            Assert.assertTrue(mutableIds.contains(attr.name()));
-            mutableIds.remove(attr.name());
-            if ("mail".equals(attr.name())) {
-                Assert.assertEquals(attr.asList().stream().map(DDF::string).toList(),
-                        CollectionSupport.singletonList("jdoe at example.org"));
-            } else if ("displayName".equals(attr.name())) {
-                Assert.assertEquals(attr.asList().stream().map(DDF::string).toList(),
-                        CollectionSupport.singletonList("John Doe"));
-            } else if ("eduPersonScopedAffiliation".equals(attr.name())) {
-                Assert.assertEquals(attr.asList().stream().map(ddf -> {return ddf.getmember("value").string();}).toList(),
-                        CollectionSupport.listOf("member"));
-                Assert.assertEquals(attr.asList().stream().map(ddf -> {return ddf.getmember("scope").string();}).toList(),
-                        CollectionSupport.listOf("example.org"));
-            }
-        }
-        Assert.assertTrue(mutableIds.isEmpty());
-        
-        try {
-            final String s = output.getmember(ConsumerConstants.SESSION_OPAQUE).string();
-            assert s != null;
-            final byte[] opaque = Base64Support.decodeURLSafe(s);
-            try (final ByteArrayInputStream in = new ByteArrayInputStream(opaque)) {
-                final XMLObject obj = XMLObjectSupport.unmarshallFromInputStream(parserPool, in);
-                if (obj instanceof final NameID nameID) {
-                    Assert.assertEquals(nameID.getValue(), "jdoe at example.org");
-                    Assert.assertEquals(nameID.getFormat(), NameIDType.EMAIL);
-                    Assert.assertEquals(nameID.getSPProvidedID(), sessionIndex);
-                } else {
-                    Assert.fail("Session data was not a NameID");
-                }
-            }
-        } catch (final DecodingException|IOException|UnmarshallingException|XMLParserException e) {
-            Assert.fail(e.getMessage());
-        }
-        
-        return output;
-    }
 
     /**
      * Builds a dummy OIDC authorization code response.
diff --git a/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/TestConstants.java b/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/TestConstants.java
index 5408668..9883156 100644
--- a/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/TestConstants.java
+++ b/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/TestConstants.java
@@ -94,6 +94,12 @@ public final class TestConstants {
     /** Target URL encoded in cookie. */
     public static final String TARGET_URL = "https://sp.example.org/secure";
     
+    /** The defaulted prefix of the correlation cookie.*/
+    public static final String CORRELATION_COOKIE_PREFIX = "__Host-shibsp_req_";
+    
+    /** The defaulted prefix for state tokens.*/
+    public static final String STATE_TOKEN_PREFIX = "__Host-shibsp_state__";
+    
     /**
      * Build cookie header bytes for the given parameters. These take the form of:
      * <pre>
@@ -122,11 +128,11 @@ public final class TestConstants {
 
         // Compose the header with semicolons and spacing as in the example
         final StringBuilder sb = new StringBuilder();
-        sb.append("__Host-shibsp_req_").append(stateToken).append('=')
+        sb.append(CORRELATION_COOKIE_PREFIX).append(stateToken).append('=')
             .append(buildCorrelationCookieString(authnStateToken, rfp)).append(";\n");
-        sb.append("__Host-shibsp_state__").append(appID).append('_').append(stateToken)
+        sb.append(STATE_TOKEN_PREFIX).append(appID).append('_').append(stateToken)
           .append('=').append(targetUrlB64).append("; \n");
-        sb.append("__Host-shibsp_state__").append(appID).append('_').append(authnStateToken).append('=').append(authnJsonEnc).append(";\n");
+        sb.append(STATE_TOKEN_PREFIX).append(appID).append('_').append(authnStateToken).append('=').append(authnJsonEnc).append(";\n");
         System.out.println(sb.toString());
         return sb.toString().getBytes("UTF-8");
         

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


More information about the commits mailing list