[java-plugin-shibd-oidc] branch main updated: JSHIBDOIDC-14 - Add validation error to DDF output

Codeberg noreply at shibboleth.net
Fri Mar 6 11:15:06 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/62866fd414b830596531f76c8ca73d0e6b7227d0

The following commit(s) were added to refs/heads/main by this push:
     new 62866fd  JSHIBDOIDC-14 - Add validation error to DDF output
62866fd is described below

commit 62866fd414b830596531f76c8ca73d0e6b7227d0
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Fri Mar 6 11:14:57 2026 +0000

    JSHIBDOIDC-14 - Add validation error to DDF output
    
     - Add the error message thrown by the failed validator to the DDF
    output.
     - Create a testing module for shared test classes.
    
    https://shibboleth.atlassian.net/browse/JSHIBDOIDC-14
---
 pom.xml                                            |   1 +
 sp-oidc-bom/pom.xml                                |   8 +-
 sp-oidc-conf-impl/pom.xml                          |   7 ++
 .../idp/flows/sp/initiator/oidc/oidc-beans.xml     |   2 +-
 .../sp/oidc/flows/OIDCTokenConsumerFlowTest.java   |  52 ++++----
 sp-oidc-impl/pom.xml                               |   7 ++
 .../sp/oidc/profile/impl/ValidateTokenClaims.java  |  20 +++
 .../oidc/profile/impl/ValidateTokenClaimsTest.java | 136 +++++++++++++++++++++
 sp-oidc-testing/pom.xml                            |  78 ++++++++++++
 .../net/shibboleth/sp/oidc/testing/TestHelper.java |  34 +++++-
 10 files changed, 307 insertions(+), 38 deletions(-)

diff --git a/pom.xml b/pom.xml
index 1ff9d48..67513dc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -52,6 +52,7 @@ copyright/license information. -->
         <module>sp-oidc-api</module>
         <module>sp-oidc-impl</module>
         <module>sp-oidc-dist</module>
+        <module>sp-oidc-testing</module>
     </modules>
 
     <dependencies>
diff --git a/sp-oidc-bom/pom.xml b/sp-oidc-bom/pom.xml
index 28726c8..1cd15dd 100644
--- a/sp-oidc-bom/pom.xml
+++ b/sp-oidc-bom/pom.xml
@@ -10,7 +10,7 @@
         <version>0.0.1-SNAPSHOT</version>
     </parent>
 
-    <name>Shibboleth SP OIDC Protocol Plugin :: BOM</name>
+    <name>Shibboleth SP :: OIDC Protocol Plugin :: BOM</name>
     <description>Bill of Materials</description>
     <artifactId>sp-oidc-bom</artifactId>
     <packaging>pom</packaging>
@@ -34,6 +34,12 @@
                 <artifactId>sp-oidc-conf-impl</artifactId>
                 <version>${project.version}</version>
             </dependency>
+            
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>sp-oidc-testing</artifactId>
+                <version>${project.version}</version>
+            </dependency>
         </dependencies>
     </dependencyManagement>
     
diff --git a/sp-oidc-conf-impl/pom.xml b/sp-oidc-conf-impl/pom.xml
index 8818d6f..91c9944 100644
--- a/sp-oidc-conf-impl/pom.xml
+++ b/sp-oidc-conf-impl/pom.xml
@@ -197,6 +197,13 @@
             <type>test-jar</type>
             <scope>test</scope>
         </dependency>
+        
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>sp-oidc-testing</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
 
         <dependency>
             <groupId>${shibd.groupId}</groupId>
diff --git a/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/initiator/oidc/oidc-beans.xml b/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/initiator/oidc/oidc-beans.xml
index 9c78a5d..b9aa165 100644
--- a/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/initiator/oidc/oidc-beans.xml
+++ b/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/initiator/oidc/oidc-beans.xml
@@ -334,7 +334,7 @@
         p:errorFatal="%{sp.stateToken.errorsFatal:true}" 
         p:stateValueLookupStrategy-ref="AuthenticationRequestStateForStorageStrategy" />
         
-        <!--  TODO TRIAL -->
+        <!--  TODO TRIAL (Ensure this is sealed)-->
     <bean id="MapAuthenticationStateToStateToken" class="net.shibboleth.sp.oidc.profile.impl.MapStateToStateToken" scope="prototype"
         p:createOutputObjects="true"
         p:errorFatal="%{sp.stateToken.errorsFatal:true}" 
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 d13494c..04ca736 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
@@ -96,6 +96,7 @@ import net.shibboleth.sp.ddf.DDF;
 import net.shibboleth.sp.flows.AbstractSPFlowTest;
 import net.shibboleth.sp.messaging.RemotedHttpServletRequest;
 import net.shibboleth.sp.messaging.RemotedHttpServletResponse;
+import net.shibboleth.sp.oidc.testing.TestHelper;
 import net.shibboleth.sp.profile.ConsumerConstants;
 
 /**
@@ -257,7 +258,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("test output: " + output.toString());
+        TestHelper.validateAssertionError(output, "No authentication time found in token");
     }
     
     /**
@@ -359,7 +362,10 @@ 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("test output: " + output.toString());
+        TestHelper.validateAssertionError(output, 
+                "Did not receive one of the requested ACR claim values. Requested '[loa1]', received 'loa2'");
 
     }
     
@@ -464,7 +470,10 @@ 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("test output: " + output.toString());
+        TestHelper.validateAssertionError(output, 
+                "JWT \"azp\" claim has value bad-azp but should be testspclientid");
     }
     
     /**
@@ -495,7 +504,10 @@ 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("test output: " + output.toString());
+        TestHelper.validateAssertionError(output, 
+                "JWT \"nonce\" claim has value bad-idtoken-nonce but should be bd1b5f211250c57e");
     }
     
     /**
@@ -651,7 +663,7 @@ public class OIDCTokenConsumerFlowTest extends AbstractSPFlowTest {
 
          
          final JWT signedIdToken = 
-                 TestTokenHelper.createJWT(claimsSet, JWSAlgorithm.RS256, null, null, opSigningCredential, null);
+                 TestHelper.createJWT(claimsSet, JWSAlgorithm.RS256, null, null, opSigningCredential, null);
          
          final AccessToken accessToken = new BearerAccessToken("fake-access-token-value", 3600, null);
          final RefreshToken refreshToken = new RefreshToken("fake-refresh-token-value");
@@ -691,7 +703,7 @@ public class OIDCTokenConsumerFlowTest extends AbstractSPFlowTest {
          final JWTClaimsSet claimsSet = claimsSetBuilder.build();
          
          final JWT encryptedIdToken = 
-                 TestTokenHelper.createJWT(claimsSet, JWSAlgorithm.RS256, JWEAlgorithm.RSA_OAEP_256, 
+                 TestHelper.createJWT(claimsSet, JWSAlgorithm.RS256, JWEAlgorithm.RSA_OAEP_256, 
                          EncryptionMethod.A128CBC_HS256, opSigningCredential, rpEncryptionCredential);
          
          final AccessToken accessToken = new BearerAccessToken("fake-access-token-value", 3600, null);
@@ -728,7 +740,7 @@ public class OIDCTokenConsumerFlowTest extends AbstractSPFlowTest {
                 .claim("nickname","John").build();
        
         final JWT signedUserInfoToken = 
-                TestTokenHelper.createJWT(claimsSet, JWSAlgorithm.RS256, null, null, opSigningCredential, null);
+                TestHelper.createJWT(claimsSet, JWSAlgorithm.RS256, null, null, opSigningCredential, null);
         return  new UserInfoSuccessResponse(signedUserInfoToken);
     }
     
@@ -764,7 +776,7 @@ public class OIDCTokenConsumerFlowTest extends AbstractSPFlowTest {
             .thenReturn(userInfoResponse);
         
         // Fetch from the keys endpoint mocking the call used by the RemoteJwkSetCache     
-        final String jwk = TestTokenHelper.createJWKJSONStringFrom(opSigningCredential);
+        final String jwk = TestHelper.createJWKJSONStringFrom(opSigningCredential);
         assertNotNull(jwk);
         final ClassicHttpResponse classicHttpResponse = Mockito.mock(ClassicHttpResponse.class);
         Mockito.when(classicHttpResponse.getCode()).thenReturn(200);
@@ -923,26 +935,7 @@ public class OIDCTokenConsumerFlowTest extends AbstractSPFlowTest {
         
         return output;
     }
-    
-    /**
-     * Tests the output contains token validation error starting with the designated string.
-     * 
-     * @param output output message
-     * @param messagePrefix error message to check for
-     */
-    private void validateAssertionError(@Nullable final DDF output, @Nullable final String messagePrefix) {
-        if (output == null) {
-            Assert.fail("No output message");
-            return;
-        }
-        
-        final DDF errors = output.getmember(ConsumerConstants.VALIDATION_ERRORS);
-        Assert.assertTrue(errors.islist());
-        
-        final String msg = errors.asList().get(0).string();
-        Assert.assertTrue(msg != null && msg.startsWith(messagePrefix));
-    }
-    
+
     /**
      * Builds a dummy OIDC authorization code response.
      * 
@@ -1011,8 +1004,7 @@ public class OIDCTokenConsumerFlowTest extends AbstractSPFlowTest {
      * 
      * @throws IOException on error 
      */
-    @Nonnull private DDF buildRemotedQueryStringResponse(@Nonnull final AuthorizationResponse response) 
-            throws IOException {                
+    @Nonnull private DDF buildRemotedQueryStringResponse(@Nonnull final AuthorizationResponse response) {                
 
         final DDF obj = new DDF(null).structure();
         obj.addmember(ConsumerConstants.BASE_URL).unsafe_string(TestConstants.RESOURCE_URL.getBytes(StandardCharsets.UTF_8));
diff --git a/sp-oidc-impl/pom.xml b/sp-oidc-impl/pom.xml
index bf65051..f98152f 100644
--- a/sp-oidc-impl/pom.xml
+++ b/sp-oidc-impl/pom.xml
@@ -176,6 +176,13 @@
             <scope>test</scope>
         </dependency>
         
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>sp-oidc-testing</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        
         <dependency>
             <groupId>${shibd.groupId}</groupId>
             <artifactId>sp-server-impl</artifactId>
diff --git a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/ValidateTokenClaims.java b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/ValidateTokenClaims.java
index 8816514..35c66c3 100644
--- a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/ValidateTokenClaims.java
+++ b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/ValidateTokenClaims.java
@@ -39,6 +39,9 @@ import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
 import net.shibboleth.shared.component.ComponentInitializationException;
 import net.shibboleth.shared.logic.Constraint;
 import net.shibboleth.shared.primitive.LoggerFactory;
+import net.shibboleth.sp.context.AgentRequestContext;
+import net.shibboleth.sp.ddf.DDF;
+import net.shibboleth.sp.profile.ConsumerConstants;
 
 /**
  * Action that validates the claims of a JWT using the supplied 
@@ -158,6 +161,23 @@ public class ValidateTokenClaims extends AbstractProfileAction {
             }
         } catch (final JWTValidationException e) {
             log.error("{} JWT claims verification failed for subject '{}'", getLogPrefix(), subject, e);
+            
+            // Capture the exception error in the validation_errors DDF member
+            final AgentRequestContext agentRequestContext =
+                    profileRequestContext.ensureSubcontext(AgentRequestContext.class);
+            DDF output = agentRequestContext.getOutput();
+            if (output == null) {
+                output = new DDF(null).structure();
+                agentRequestContext.setOutput(output);
+            }
+            
+            final DDF errors = output.addmember(ConsumerConstants.VALIDATION_ERRORS);
+            if (!errors.islist()) {
+                errors.list();
+            }
+            errors.add(new DDF(null).string(e.getMessage()));
+            log.trace("{} Added validation_error for the agent: {}", getLogPrefix(), e.getMessage());
+            
             ActionSupport.buildEvent(profileRequestContext, OidcEventIds.INVALID_TOKEN);
             if (cleanupHook != null) {
                 cleanupHook.accept(profileRequestContext);
diff --git a/sp-oidc-impl/src/test/java/net/shibboleth/sp/oidc/profile/impl/ValidateTokenClaimsTest.java b/sp-oidc-impl/src/test/java/net/shibboleth/sp/oidc/profile/impl/ValidateTokenClaimsTest.java
new file mode 100644
index 0000000..a93cf59
--- /dev/null
+++ b/sp-oidc-impl/src/test/java/net/shibboleth/sp/oidc/profile/impl/ValidateTokenClaimsTest.java
@@ -0,0 +1,136 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.sp.oidc.profile.impl;
+
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+
+import java.util.Map;
+
+import org.mockito.Mockito;
+import org.springframework.webflow.execution.Event;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.nimbusds.jwt.JWTClaimsSet;
+import com.nimbusds.jwt.PlainJWT;
+
+import net.shibboleth.idp.authn.AuthnEventIds;
+import net.shibboleth.idp.profile.testing.ActionTestingSupport;
+import net.shibboleth.oidc.jwt.claims.ClaimsValidator;
+import net.shibboleth.oidc.jwt.claims.JWTValidationException;
+import net.shibboleth.oidc.profile.core.OidcEventIds;
+import net.shibboleth.shared.component.ComponentInitializationException;
+import net.shibboleth.sp.ddf.DDF;
+import net.shibboleth.sp.oidc.testing.TestHelper;
+import net.shibboleth.sp.profile.impl.BaseAgplicationActionTest;
+
+/**
+ * Tests for {@link ValidateTokenClaims}
+ */
+public class ValidateTokenClaimsTest extends BaseAgplicationActionTest{
+    
+    /** The action to test. */
+    private ValidateTokenClaims action;
+    
+    @SuppressWarnings("null")
+    @Override
+    @BeforeMethod
+    public void beforeMethod() throws ComponentInitializationException {
+        super.beforeMethod();        
+        action = new ValidateTokenClaims();     
+
+    }
+    
+    /**
+     * Validate a valid token. The mocked verifier always returns without throwing a validation exception
+     * irrespective of the input.
+     * 
+     * @throws ComponentInitializationException on error.
+     */
+    @Test
+    public final void testValidToken() throws ComponentInitializationException {
+        
+        //default mock will just return
+        final ClaimsValidator verifier = 
+                Mockito.mock(ClaimsValidator.class);       
+        action.setClaimsValidator(verifier);
+        
+        final JWTClaimsSet claims = TestHelper.createBasicClaims(Map.of());        
+        action.setJwtLookupStrategy(prcFunct -> new PlainJWT(claims));    
+        action.initialize();
+        
+        final Event event = action.execute(src);
+        // null event is success.
+        assertNull(event);
+    }
+    
+    /**
+     * Validate an invalid token. The token fails its audience checks.
+     * 
+     * @throws ComponentInitializationException on error.
+     */
+    @Test
+    public final void testInValidToken() throws Exception {
+        
+        //default mock will just return
+        final ClaimsValidator verifier = 
+                Mockito.mock(ClaimsValidator.class);            
+        action.setClaimsValidator(verifier);
+        
+        final JWTClaimsSet claims = TestHelper.createBasicClaims(Map.of());  
+        final var jwt = new PlainJWT(claims);        
+
+        Mockito.doThrow(new JWTValidationException("Bad audience"))
+            .when(verifier).validate(Mockito.any(), Mockito.any());
+
+              
+        action.setJwtLookupStrategy(prcFunct -> jwt);    
+        action.initialize();
+        
+        final Event event = action.execute(src);
+        // null event is success.
+        assertNotNull(event);
+        ActionTestingSupport.assertEvent(event, OidcEventIds.INVALID_TOKEN);
+        final DDF output = arc.getOutput();
+        assertNotNull(output);
+        assert output != null;
+        TestHelper.validateAssertionError(output, "Bad audience");
+        
+    }
+    
+    /**
+     * Test there is an invalid context if no JWT is present.
+     * 
+     * @throws ComponentInitializationException on error.
+     */
+    @Test
+    public final void testNoToken() throws ComponentInitializationException {
+        
+        //default mock will just return
+        final ClaimsValidator verifier = 
+                Mockito.mock(ClaimsValidator.class);       
+        action.setClaimsValidator(verifier);
+        
+        action.setJwtLookupStrategy(prcFunct -> null);    
+        action.initialize();
+        
+        final Event event = action.execute(src);
+        // null event is success.
+        assertNotNull(event);
+        ActionTestingSupport.assertEvent(event, AuthnEventIds.INVALID_AUTHN_CTX);
+    }
+
+}
diff --git a/sp-oidc-testing/pom.xml b/sp-oidc-testing/pom.xml
new file mode 100644
index 0000000..d88b86d
--- /dev/null
+++ b/sp-oidc-testing/pom.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- See LICENSE.txt file in the root directory of this repository for the
+copyright/license information. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>net.shibboleth.sp</groupId>
+        <artifactId>sp-oidc-parent</artifactId>
+        <version>0.0.1-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>sp-oidc-testing</artifactId>
+    <name>Shibboleth SP :: OIDC Protocol Plugin :: Testing API</name>
+    <description>Testing APIs for OIDC SP components</description>
+    <packaging>jar</packaging>
+
+    <properties>
+        <checkstyle.configLocation>${project.basedir}/../resources/checkstyle/checkstyle.xml</checkstyle.configLocation>
+        <automatic.module.name>net.shibboleth.sp.oidc.testing</automatic.module.name>
+    </properties>
+
+    <dependencies>
+        <!-- Compile dependencies -->
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>sp-oidc-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>sp-server-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.testng</groupId>
+            <artifactId>testng</artifactId>
+            <scope>compile</scope>
+        </dependency>        
+        <!-- Provided dependencies -->
+        <dependency>
+            <groupId>${shib-profile.groupId}</groupId>
+            <artifactId>shib-profile-impl</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>${oidc-common.groupId}</groupId>
+            <artifactId>oidc-common-crypto-impl</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>${opensaml.groupId}</groupId>
+            <artifactId>opensaml-core-impl</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>${oidc-common.groupId}</groupId>
+            <artifactId>oidc-common-crypto-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.datatype</groupId>
+            <artifactId>jackson-datatype-jdk8</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-core</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+</project>
\ No newline at end of file
diff --git a/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/TestTokenHelper.java b/sp-oidc-testing/src/main/java/net/shibboleth/sp/oidc/testing/TestHelper.java
similarity index 94%
rename from sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/TestTokenHelper.java
rename to sp-oidc-testing/src/main/java/net/shibboleth/sp/oidc/testing/TestHelper.java
index dcaffa9..9c77fbb 100644
--- a/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/TestTokenHelper.java
+++ b/sp-oidc-testing/src/main/java/net/shibboleth/sp/oidc/testing/TestHelper.java
@@ -11,9 +11,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-
-package net.shibboleth.sp.oidc.flows;
+package net.shibboleth.sp.oidc.testing;
 
 import java.security.PublicKey;
 import java.security.interfaces.ECPrivateKey;
@@ -29,6 +27,7 @@ import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
 import org.opensaml.security.credential.Credential;
+import org.testng.Assert;
 
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
@@ -58,15 +57,38 @@ import com.nimbusds.jwt.SignedJWT;
 
 import net.shibboleth.oidc.security.credential.JWKCredential;
 import net.shibboleth.shared.collection.CollectionSupport;
+import net.shibboleth.sp.ddf.DDF;
+import net.shibboleth.sp.profile.ConsumerConstants;
 
-/** A helper class for generating tokens for testing.*/
-public final class TestTokenHelper {
+/** A helper class for testing.*/
+public final class TestHelper {
     
     /** Private constructor.*/
-    private TestTokenHelper() {
+    private TestHelper() {
+        
+    }
+    
+    
+    /**
+     * Tests the output contains token validation error starting with the designated string.
+     * 
+     * @param output output message
+     * @param messagePrefix error message to check for
+     */
+    public static void validateAssertionError(@Nullable final DDF output, @Nullable final String messagePrefix) {
+        if (output == null) {
+            Assert.fail("No output message");
+            return;
+        }
+        
+        final DDF errors = output.getmember(ConsumerConstants.VALIDATION_ERRORS);
+        Assert.assertTrue(errors.islist());
         
+        final String msg = errors.asList().get(0).string();
+        Assert.assertTrue(msg != null && msg.startsWith(messagePrefix));
     }
     
+    
     /**
      * Build a basic {@link JWTClaimsSet} from the supplied parameters.
      * 

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


More information about the commits mailing list