[java-plugin-shibd-oidc] branch main updated: JSHIBDOIDC-8 - Add back missing token validation logic

Codeberg noreply at shibboleth.net
Fri Jan 30 14:48:31 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/ee6d5205f76eeba8ba91c94d99a0eed953955247

The following commit(s) were added to refs/heads/main by this push:
     new ee6d520  JSHIBDOIDC-8 - Add back missing token validation logic
ee6d520 is described below

commit ee6d5205f76eeba8ba91c94d99a0eed953955247
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Fri Jan 30 14:48:19 2026 +0000

    JSHIBDOIDC-8 - Add back missing token validation logic
    
     - Add requested_acrs to the authentication request state
     - Enable the acr validator
     - add activation condition to the validator so it can be disabled from
    the profile config
     - Add tests
    
    https://shibboleth.atlassian.net/browse/JSHIBDOIDC-8
---
 pom.xml                                            |  12 +--
 .../profile/AuthenticationRequestStateData.java    |   4 +-
 .../idp/flows/sp/consumer/oidc/oidc-beans.xml      |  34 +++---
 .../net/shibboleth/sp/service/agent/postconfig.xml |   5 +-
 .../sp/oidc/flows/OIDCTokenConsumerFlowTest.java   | 118 +++++++++++++++++++--
 .../shibboleth/sp/oidc/flows/TestConstants.java    |   9 +-
 ...DCEnvironmentApplicationContextInitializer.java |   2 +
 .../idp/module/conf/attribute-filter.xml           |   1 +
 .../module/conf/attributes/oidc-claim-rules.xml    |  11 ++
 .../idp/module/conf/sp/oidc-test.properties        |   2 +
 .../profile/impl/ProcessCorrelationCookie.java     |   2 +-
 .../sp/oidc/profile/impl/ProcessEndUserClaims.java |  22 +++-
 .../RequestedACRClaimsFromStateLookupStrategy.java |  80 ++++++++++++++
 .../RequestedACRValidationActivationCondition.java |  83 +++++++++++++++
 ...idationClaimStrippingSanitizationStrategy.java} |   9 +-
 ...uestedACRClaimsFromStateLookupStrategyTest.java |  97 +++++++++++++++++
 ...uestedACRValidationActivationConditionTest.java |  79 ++++++++++++++
 17 files changed, 525 insertions(+), 45 deletions(-)

diff --git a/pom.xml b/pom.xml
index 0a20d17..f9a418f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -20,21 +20,21 @@ copyright/license information. -->
     <properties>
         <shibboleth.projectName>java-plugin-shibd-oidc</shibboleth.projectName>
         <shib-shared.groupId>net.shibboleth</shib-shared.groupId>
-        <shib-shared.version>9.2.0-SNAPSHOT</shib-shared.version>
+        <shib-shared.version>9.2.0</shib-shared.version>
         <opensaml.groupId>org.opensaml</opensaml.groupId>
-        <opensaml.version>5.2.0-SNAPSHOT</opensaml.version>
+        <opensaml.version>5.2.0</opensaml.version>
         <shib-metadata.groupId>net.shibboleth</shib-metadata.groupId>
-        <shib-metadata.version>5.2.0-SNAPSHOT</shib-metadata.version>
+        <shib-metadata.version>5.2.0</shib-metadata.version>
         <shib-attribute.groupId>net.shibboleth</shib-attribute.groupId>
-        <shib-attribute.version>5.2.0-SNAPSHOT</shib-attribute.version>
+        <shib-attribute.version>5.2.0</shib-attribute.version>
         <shib-profile.groupId>net.shibboleth</shib-profile.groupId>
-        <shib-profile.version>5.2.0-SNAPSHOT</shib-profile.version>
+        <shib-profile.version>5.2.0</shib-profile.version>
         <oidc-common.groupId>net.shibboleth.oidc</oidc-common.groupId>
         <oidc-common.version>3.3.1-SNAPSHOT</oidc-common.version>
         <oidc-config.groupId>net.shibboleth.idp.plugin.config.oidc</oidc-config.groupId>
         <oidc-config.version>3.0.0</oidc-config.version>
         <idp.groupId>net.shibboleth.idp</idp.groupId>
-        <idp.version>5.2.0-SNAPSHOT</idp.version>
+        <idp.version>5.2.0</idp.version>
         <shibd.groupId>net.shibboleth.sp</shibd.groupId>
         <shibd.version>0.0.1-SNAPSHOT</shibd.version>
         <checkstyle.configLocation>
diff --git a/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/profile/AuthenticationRequestStateData.java b/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/profile/AuthenticationRequestStateData.java
index ee9cec6..d61f210 100644
--- a/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/profile/AuthenticationRequestStateData.java
+++ b/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/profile/AuthenticationRequestStateData.java
@@ -254,7 +254,7 @@ public class AuthenticationRequestStateData {
      */
     @JsonProperty("requested_acrs")
     @Nonnull @Unmodifiable @NotLive public List<String> getAcrs() {
-        return acrs;
+        return CollectionSupport.copyToList(acrs);
     }
     
     /**
@@ -325,7 +325,7 @@ public class AuthenticationRequestStateData {
     }
     
     /**
-     * A simple method to mask all but the last 2 characters of a string value for logging purposes.
+     * A method to mask all but the last 2 characters of a string value for logging purposes.
      * 
      * @param value the string value to mask
      * @return the masked string
diff --git a/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/consumer/oidc/oidc-beans.xml b/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/consumer/oidc/oidc-beans.xml
index 7cdcd9f..2855bdb 100644
--- a/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/consumer/oidc/oidc-beans.xml
+++ b/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/consumer/oidc/oidc-beans.xml
@@ -273,7 +273,7 @@
         <ref bean="NonceClaimValidator" />
         <ref bean="AtHashValidator"/>
         <ref bean="AuthenticationTimeClaimValidator"/>
-        <!-- <ref bean="ACRClaimValidator"/> should we just pass these back to the agent-->
+        <ref bean="ACRClaimValidator"/>
     </util:list>
 
     <bean id="IDTokenRequiredClaimsValidator"
@@ -353,10 +353,10 @@
         p:activationCondition="#{getObject('sp.oidc.jwt.NonceActivationCondition') ?: 
                                 getObject('DefaultNonceActivationCondition')}" />
 
-     <bean id="DefaultNonceActivationCondition"
+     <bean id="DefaultNonceActivationCondition" scope="prototype"
         class="net.shibboleth.oidc.security.jwt.claims.impl.NonceValidationActivationCondition" />
 
-     <bean id="DefaultNonceLookupStrategy"
+     <bean id="DefaultNonceLookupStrategy" scope="prototype"
         class="net.shibboleth.sp.oidc.profile.impl.NonceFromAuthenticationRequestStateLookupStrategy" />
 
     <bean id="OIDCMetadataContextChildLookup" class="org.opensaml.messaging.context.navigate.ChildContextLookup"
@@ -372,10 +372,10 @@
         p:joseHeaderLookupStrategy="#{getObject('%{sp.oidc.jwt.IDTokenJOSEHeaderLookupStrategy:}') ?: 
                                 getObject('DefaultIDTokenJOSEHeaderLookupStrategy')}"/>
                                 
-    <bean id="DefaultAccessTokenLookupStrategy" 
+    <bean id="DefaultAccessTokenLookupStrategy" scope="prototype"
         class="net.shibboleth.oidc.profile.context.navigate.AccessTokenLookupStrategy"/>
     
-    <bean id="DefaultIDTokenJOSEHeaderLookupStrategy" 
+    <bean id="DefaultIDTokenJOSEHeaderLookupStrategy" scope="prototype"
         class="net.shibboleth.oidc.profile.context.navigate.IDTokenJOSEHeaderLookupStrategy"/>
     
     <bean id="AuthenticationTimeClaimValidator" 
@@ -390,24 +390,30 @@
         class="net.shibboleth.sp.oidc.profile.impl.MaxAgeFromAuthenticationRequestLookupFunction"
         c:maxAgeDefault="%{sp.oidc.idtoken.jwt.verifier.authnLifetime:PT60S}"/>
         
-   <bean id="AuthenticationRequestTimeLookupFunction" 
+   <bean id="AuthenticationRequestTimeLookupFunction" scope="prototype"
         class="net.shibboleth.sp.oidc.profile.impl.AuthenticationRequestTimeLookupFunction"
         p:clockSkew="%{sp.oidc.idtoken.jwt.verifier.authnRequestClockSkew:PT0S}"
         p:authenticationRequestLookupStrategy-ref="shibboleth.ChildLookup.AuthenticationRequestStateDataFromInbound"/> 
 
-   <bean id="DefaultAuthTimeActivationCondition" 
+   <bean id="DefaultAuthTimeActivationCondition" scope="prototype"
         class="net.shibboleth.sp.oidc.profile.impl.AuthTimeRequestedActivationCondition"
         c:authenticationRequestLookupStrategy-ref="shibboleth.ChildLookup.AuthenticationRequestStateDataFromInbound"/>
 
  
-    <!-- <bean id="ACRClaimValidator" class="net.shibboleth.oidc.security.jwt.claims.impl.ACRClaimsValidator"
+    <bean id="ACRClaimValidator" class="net.shibboleth.oidc.security.jwt.claims.impl.ACRClaimsValidator" scope="prototype"
         p:requestedEssentialAcrsClaimLookupStrategy="#{getObject('sp.oidc.jwt.RequestedEssentialAcrsClaimLookupStrategy') ?: 
-                                getObject('DefaultRequestedEssentialAcrsClaimLookupStrategy')}"/>
+                                getObject('DefaultRequestedACRClaimLookupStrategy')}"
+        p:activationCondition="#{getObject('sp.oidc.jwt.AcrActivationCondition') ?: 
+                                getObject('DefaultRequestedACRValidationActivationCondition')}"/>
+    
+    <bean id="DefaultRequestedACRClaimLookupStrategy" scope="prototype"
+                class="net.shibboleth.sp.oidc.profile.impl.RequestedACRClaimsFromStateLookupStrategy"
+                c:authenticationRequestLookupStrategy-ref="shibboleth.ChildLookup.AuthenticationRequestStateDataFromInbound"/> 
+    
+    <bean id="DefaultRequestedACRValidationActivationCondition" scope="prototype"
+        class="net.shibboleth.sp.oidc.profile.impl.RequestedACRValidationActivationCondition"
+        c:relyingPartyContextLookupStrategy-ref="shibboleth.ChildLookup.RelyingParty"/>
     
-    <bean id="DefaultRequestedEssentialAcrsClaimLookupStrategy" 
-                class="net.shibboleth.oidc.security.jwt.claims.impl.RequestedEssentialACRClaimsLookupStrategy"
-                c:authenticationRequestLookupStrategy-ref="shibboleth.ChildLookup.MessageLookup.Outbound.OIDCAuthenticationRequest"/> 
-     -->
     <!-- - End ID Token Claims Validation -->
     
     <bean id="CheckUserInfoRequiredCondition" class=" net.shibboleth.oidc.profile.config.logic.UserInfoLookupPredicate" />
@@ -445,7 +451,7 @@
         class="net.shibboleth.sp.oidc.profile.impl.DefaultClaimMergingStrategy" />
 
     <bean id="DefaultClaimSanitizationStrategy"
-        class="net.shibboleth.sp.oidc.profile.impl.DefaultClaimSanitizationStrategy" />
+        class="net.shibboleth.sp.oidc.profile.impl.ValidationClaimStrippingSanitizationStrategy" />
         
     <!-- Will populate the same security params context as the id_token, but overwrite the decryption config. -->
     <bean id="PopulateUserInfoDecryptionParameters"
diff --git a/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/sp/service/agent/postconfig.xml b/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/sp/service/agent/postconfig.xml
index 10868ba..443f3ed 100644
--- a/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/sp/service/agent/postconfig.xml
+++ b/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/sp/service/agent/postconfig.xml
@@ -25,6 +25,7 @@
         p:tokenConsumers="#{{'oidc/code/query', 'oidc/code/post'}}" />
 
     <!-- Credential bean factories -->
+    <!--  TODO should these be here? -->
     <bean id="shibboleth.oidc.ClientSecretCredential" abstract="true"
         class="net.shibboleth.oidc.security.credential.BasicClientSecretCredentialFactoryBean" />    
         
@@ -65,6 +66,7 @@
     <bean id="AbstractOIDCProfile" abstract="true"
         p:securityConfiguration-ref="%{sp.security.oidc.config:shibboleth.oidc.DefaultSecurityConfiguration}" />
 
+<!-- TODO make sure the idtoken properties are idToken, and what to do with all the OP specific settings -->
     <bean id="AbstractOIDCSSOProfile" parent="AbstractOIDCProfile" abstract="true" 
         p:issuer="#{getObject('shibboleth.oidc.issuer')}"        
         p:tokenEndpointAuthMethods="%{sp.oidc.tokenEndpointAuthMethods:client_secret_basic,client_secret_post,client_secret_jwt,private_key_jwt}"
@@ -102,10 +104,11 @@
         p:signRequestObject="%{sp.oidc.requestobject.signed:true}"
         p:useRequestObject="%{sp.oidc.requestobject.used:false}"
         p:responseMode="%{sp.oidc.responseMode:#{null}}"
-        p:tlsServerValidationSufficient="%{sp.oidc.idtoken.tlsServerValidationOnly:false}"
+        p:tlsServerValidationSufficient="%{sp.oidc.idToken.tlsServerValidationOnly:false}"
         p:userInfoHttpRequestMethod="%{sp.oidc.userinfo.httpRequestMethod:GET}"
         p:scopes="%{sp.oidc.scopes:#{null}}"
         p:responseModes="%{sp.oauth2.responseModes:}"
+        p:shouldValidateAcrValue="%{sp.oidc.idToken.validateAcrValue:true}"
         p:requireAuthenticationRequestPredicate-ref="%{sp.oauth2.requireAuthenticationRequestPredicate:shibboleth.Conditions.TRUE}">
     </bean>
     
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 c20c156..cc58fb5 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
@@ -170,7 +170,7 @@ public class OIDCTokenConsumerFlowTest extends AbstractSPFlowTest {
                 TestConstants.APPLICATION_ID,
                 TestConstants.AUTHENTICATION_NONCE,
                 TestConstants.TARGET_URL,
-                TestConstants.buildAuthenticationState(null, false)));
+                TestConstants.buildAuthenticationState(null, false, null)));
         
         setApplicationRequest(TestConstants.APPLICATION_ID, input);
 
@@ -206,7 +206,7 @@ public class OIDCTokenConsumerFlowTest extends AbstractSPFlowTest {
                 TestConstants.APPLICATION_ID,
                 TestConstants.AUTHENTICATION_NONCE,
                 TestConstants.TARGET_URL,
-                TestConstants.buildAuthenticationState(Duration.ofMinutes(1), true)));
+                TestConstants.buildAuthenticationState(Duration.ofMinutes(1), true, null)));
         
         setApplicationRequest(TestConstants.APPLICATION_ID, input);
 
@@ -241,7 +241,7 @@ public class OIDCTokenConsumerFlowTest extends AbstractSPFlowTest {
                 TestConstants.APPLICATION_ID,
                 TestConstants.AUTHENTICATION_NONCE,
                 TestConstants.TARGET_URL,
-                TestConstants.buildAuthenticationState(Duration.ofMinutes(1), true)));
+                TestConstants.buildAuthenticationState(Duration.ofMinutes(1), true, null)));
         
         setApplicationRequest(TestConstants.APPLICATION_ID, input);
 
@@ -272,7 +272,7 @@ public class OIDCTokenConsumerFlowTest extends AbstractSPFlowTest {
                 TestConstants.APPLICATION_ID,
                 TestConstants.AUTHENTICATION_NONCE,
                 TestConstants.TARGET_URL,
-                TestConstants.buildAuthenticationState(null, false)));
+                TestConstants.buildAuthenticationState(null, false, null)));
         
         setApplicationRequest(TestConstants.APPLICATION_ID, input);
 
@@ -307,7 +307,7 @@ public class OIDCTokenConsumerFlowTest extends AbstractSPFlowTest {
                 TestConstants.APPLICATION_ID,
                 TestConstants.AUTHENTICATION_NONCE,
                 TestConstants.TARGET_URL,
-                TestConstants.buildAuthenticationState(null, false)));
+                TestConstants.buildAuthenticationState(null, false, null)));
         
         setApplicationRequest(TestConstants.APPLICATION_ID, input);
 
@@ -321,6 +321,78 @@ public class OIDCTokenConsumerFlowTest extends AbstractSPFlowTest {
                 TestConstants.RESOURCE_URL);
     }
     
+    /**
+     * Test failure flow with a signed id_token and a JWT user info response. The id_token contains ACR values which
+     * do not match those in the request, and ACR checking is enabled. Request 'loa1' get back 'loa2' in the id_token.
+     * 
+     * @throws IOException on error
+     */
+    @Test
+    public void testFail_SignedIDTokenWithACR_JWTUserInfo() throws Exception {
+        
+        mockOIDCEndpoints(constructSuccessfulTokenResponseSigned(Instant.now().plusSeconds(3600), Instant.now(), 
+                null, Map.of("acr","loa2")), constructJWTUserInfoResponseSigned());     
+
+        final AuthenticationSuccessResponse response = 
+                buildOIDCAuthorizationCodeResponse(TestConstants.RESPONSE_URL, ResponseMode.QUERY, TestConstants.STATE_STRING);        
+        final DDF input = buildRemotedQueryStringResponse(response);
+        
+        // Add cookies
+        input.addmember("http.headers.Cookie").unsafe_string(TestConstants.buildCookieHeader(
+                TestConstants.AUTHENTICATION_STATE, 
+                TestConstants.APPLICATION_ID,
+                TestConstants.AUTHENTICATION_NONCE,
+                TestConstants.TARGET_URL,
+                TestConstants.buildAuthenticationState(null, false, CollectionSupport.listOf("loa1"))));
+        
+        setApplicationRequest(TestConstants.APPLICATION_ID, input);
+
+        final FlowExecutionResult result = flowExecutor.launchExecution(TestConstants.FLOW_ID, null, externalContext);
+        assertFlowExecutionResult(result, TestConstants.FLOW_ID);
+        assertFlowExecutionOutcome(result.getOutcome());
+        assertOutputMessageEvent(result, EventIds.MESSAGE_PROC_ERROR);
+
+    }
+    
+    /**
+     * Test success flow with a signed id_token and a JWT user info response. The id_token contains ACR values which
+     * match those in the request, and ACR checking is enabled. Request 'loa1' get back 'loa1' in the id_token.
+     * 
+     * <p>Also test it appears in the set of outbound attributes.</p>
+     * 
+     * @throws IOException on error
+     */
+    @Test
+    public void testSuccess_SignedIDTokenWithACR_JWTUserInfo() throws Exception {
+        
+        mockOIDCEndpoints(constructSuccessfulTokenResponseSigned(Instant.now().plusSeconds(3600), Instant.now(), 
+                null, Map.of("acr","loa1")), constructJWTUserInfoResponseSigned());     
+
+        final AuthenticationSuccessResponse response = 
+                buildOIDCAuthorizationCodeResponse(TestConstants.RESPONSE_URL, ResponseMode.QUERY, TestConstants.STATE_STRING);        
+        final DDF input = buildRemotedQueryStringResponse(response);
+        
+        // Add cookies
+        input.addmember("http.headers.Cookie").unsafe_string(TestConstants.buildCookieHeader(
+                TestConstants.AUTHENTICATION_STATE, 
+                TestConstants.APPLICATION_ID,
+                TestConstants.AUTHENTICATION_NONCE,
+                TestConstants.TARGET_URL,
+                TestConstants.buildAuthenticationState(null, false, CollectionSupport.listOf("loa1"))));
+        
+        setApplicationRequest(TestConstants.APPLICATION_ID, input);
+
+        final FlowExecutionResult result = flowExecutor.launchExecution(TestConstants.FLOW_ID, null, externalContext);
+        assertFlowExecutionResult(result, TestConstants.FLOW_ID);
+        assertFlowExecutionOutcome(result.getOutcome());
+        final DDF output = assertOutputMessageSuccess(result);
+        assert output != null;
+        System.out.println("test output: " + output.toString());
+        validateOutputMessage(result, CollectionSupport.setOf("sub","mail","displayName","eduPersonScopedAffiliation",
+                "acr"), TestConstants.RESOURCE_URL);
+
+    }
+    
     /**
      * Test successful flow, where id_token contains an AZP claim.
      * 
@@ -342,7 +414,7 @@ public class OIDCTokenConsumerFlowTest extends AbstractSPFlowTest {
                 TestConstants.APPLICATION_ID,
                 TestConstants.AUTHENTICATION_NONCE,
                 TestConstants.TARGET_URL,
-                TestConstants.buildAuthenticationState(null, false)));
+                TestConstants.buildAuthenticationState(null, false, null)));
         
         setApplicationRequest(TestConstants.APPLICATION_ID, input);
 
@@ -376,7 +448,7 @@ public class OIDCTokenConsumerFlowTest extends AbstractSPFlowTest {
                 TestConstants.APPLICATION_ID,
                 TestConstants.AUTHENTICATION_NONCE,
                 TestConstants.TARGET_URL,
-                TestConstants.buildAuthenticationState(null, false)));
+                TestConstants.buildAuthenticationState(null, false, null)));
 
         setApplicationRequest(TestConstants.APPLICATION_ID, input);
 
@@ -407,7 +479,7 @@ public class OIDCTokenConsumerFlowTest extends AbstractSPFlowTest {
                 TestConstants.APPLICATION_ID,
                 TestConstants.AUTHENTICATION_NONCE,
                 TestConstants.TARGET_URL,
-                TestConstants.buildAuthenticationState(null, false)));
+                TestConstants.buildAuthenticationState(null, false, null)));
 
         setApplicationRequest(TestConstants.APPLICATION_ID, input);
 
@@ -438,7 +510,7 @@ public class OIDCTokenConsumerFlowTest extends AbstractSPFlowTest {
                 TestConstants.APPLICATION_ID,
                 TestConstants.AUTHENTICATION_NONCE,
                 TestConstants.TARGET_URL,
-                TestConstants.buildAuthenticationState(null, false)));
+                TestConstants.buildAuthenticationState(null, false, null)));
 
         setApplicationRequest(TestConstants.APPLICATION_ID, input);
 
@@ -469,7 +541,7 @@ public class OIDCTokenConsumerFlowTest extends AbstractSPFlowTest {
                 TestConstants.APPLICATION_ID,
                 "bad-nonce", //this is a bad nonce
                 TestConstants.TARGET_URL,
-                TestConstants.buildAuthenticationState(null, false)));
+                TestConstants.buildAuthenticationState(null, false, null)));
 
         setApplicationRequest(TestConstants.APPLICATION_ID, input);
 
@@ -495,7 +567,7 @@ public class OIDCTokenConsumerFlowTest extends AbstractSPFlowTest {
                 TestConstants.APPLICATION_ID,
                 TestConstants.AUTHENTICATION_NONCE,
                 TestConstants.TARGET_URL,
-                TestConstants.buildAuthenticationState(null, false)));
+                TestConstants.buildAuthenticationState(null, false, null)));
 
         setApplicationRequest(TestConstants.APPLICATION_ID, input);
 
@@ -706,6 +778,30 @@ public class OIDCTokenConsumerFlowTest extends AbstractSPFlowTest {
                         CollectionSupport.listOf("example.org"));
             }
         }
+        Assert.assertTrue(mutableIds.isEmpty());
+        
+//        try {
+//            final DDF s = output.getmember(ConsumerConstants.SESSION_OPAQUE);
+//            assert s != null;
+//            Assert.assertTrue(s.isstruct());
+//            final DDF nameIdDdf = s.getmember("nameID");
+//            Assert.assertTrue(nameIdDdf.isstring());
+//            final String nameIdString = nameIdDdf.string();
+//            assert nameIdString != null;
+//            final byte[] opaque = Base64Support.decodeURLSafe(nameIdString);
+//            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;
         
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 aaa9845..c6704bc 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
@@ -20,8 +20,10 @@ import java.net.URLEncoder;
 import java.nio.charset.StandardCharsets;
 import java.time.Duration;
 import java.time.Instant;
+import java.util.List;
 
 import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 
 import com.fasterxml.jackson.annotation.JsonInclude.Include;
 import com.fasterxml.jackson.core.Base64Variants;
@@ -124,7 +126,8 @@ public final class TestConstants {
         
     }
     
-    public static String buildAuthenticationState(final Duration maxAge, final boolean authTimeRequired) {
+    public static String buildAuthenticationState(final Duration maxAge, final boolean authTimeRequired, 
+            @Nullable final List<String> acrs) {
         final AuthenticationRequestStateData state = new AuthenticationRequestStateData()
                 .setClientId(APPLICATION_ID)
                 .setNonce(ID_TOKEN_NONCE)
@@ -133,6 +136,10 @@ public final class TestConstants {
                 .setAuthTimeRequired(authTimeRequired)
                 .setAuthnRequestTime(Instant.now());
         
+        if (acrs != null) {
+            state.setAcrs(acrs);
+        }
+        
         final ObjectMapper jsonMapper = JsonMapper.builder()
                 .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
                 .serializationInclusion(Include.NON_ABSENT)
diff --git a/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/TestSPOIDCEnvironmentApplicationContextInitializer.java b/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/TestSPOIDCEnvironmentApplicationContextInitializer.java
index c7b6512..928d72e 100644
--- a/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/TestSPOIDCEnvironmentApplicationContextInitializer.java
+++ b/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/TestSPOIDCEnvironmentApplicationContextInitializer.java
@@ -47,6 +47,8 @@ public class TestSPOIDCEnvironmentApplicationContextInitializer extends TestSPEn
         mock.setProperty("sp.agent.authn.method", "basic");
         // Resolve attributes, to test attribute resolution
         mock.setProperty("sp.oidc.resolveAttributes", "true");
+        // Ensure ACR checks are performed
+        mock.setProperty("sp.oidc.idtoken.validateAcrValue", "true");
         // Turn off the sealing of authentication request state for tests
         mock.setProperty("sp.oidc.sealAuthenticationState", "false");
         mock.setProperty("idp.additionalProperties",
diff --git a/sp-oidc-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/attribute-filter.xml b/sp-oidc-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/attribute-filter.xml
index 1342d8c..ab8cac9 100644
--- a/sp-oidc-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/attribute-filter.xml
+++ b/sp-oidc-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/attribute-filter.xml
@@ -20,6 +20,7 @@
     <AttributeFilterPolicy id="alwaysRelease">
         <PolicyRequirementRule xsi:type="Issuer" value="https://op.example.org" />
         <AttributeRule attributeID="sub" permitAny="true" />
+        <AttributeRule attributeID="acr" permitAny="true" />
         <AttributeRule attributeID="mail" permitAny="true" />
         <AttributeRule attributeID="displayName" permitAny="true" />
         <AttributeRule attributeID="eduPersonScopedAffiliation">
diff --git a/sp-oidc-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/attributes/oidc-claim-rules.xml b/sp-oidc-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/attributes/oidc-claim-rules.xml
index 9de853b..e55dd2f 100644
--- a/sp-oidc-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/attributes/oidc-claim-rules.xml
+++ b/sp-oidc-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/attributes/oidc-claim-rules.xml
@@ -420,6 +420,17 @@
                 </props>
             </property>
         </bean>
+        
+        <bean parent="shibboleth.TranscodingProperties">
+            <property name="properties">
+                <props merge="true">
+                    <prop key="id">acr</prop>
+                    <prop key="transcoder">OIDCStringTranscoder</prop>
+                    <prop key="oidc.name">acr</prop>
+                    <prop key="displayName.en">Authentication Context Class</prop>
+                </props>
+            </property>
+        </bean>
 
     </list>
     </constructor-arg>
diff --git a/sp-oidc-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/sp/oidc-test.properties b/sp-oidc-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/sp/oidc-test.properties
index f374c9b..f8b2493 100644
--- a/sp-oidc-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/sp/oidc-test.properties
+++ b/sp-oidc-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/sp/oidc-test.properties
@@ -22,5 +22,7 @@ sp.oidc.redirecturl.allowedOrigins = http://localhost
 ## TEST ENC FOR NOW from profile config
 idp.oidc.requestobject.encrypted = false
 
+#sp.oidc.idtoken.validateAcrValue = true
+
 #idp.oidc.encryption.config = dummy.oidc.EncryptionConfiguration
 
diff --git a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/ProcessCorrelationCookie.java b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/ProcessCorrelationCookie.java
index 87ec9df..2a62ef4 100644
--- a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/ProcessCorrelationCookie.java
+++ b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/ProcessCorrelationCookie.java
@@ -62,7 +62,7 @@ import net.shibboleth.sp.profile.AbstractApplicationAction;
 public class ProcessCorrelationCookie extends AbstractApplicationAction {
     
     /** Default cookie prefix. */
-    @Nonnull @NotEmpty static public final String DEFAULT_COOKIE_PREFIX = "_shibsp_req_";
+    @Nonnull @NotEmpty public static final String DEFAULT_COOKIE_PREFIX = "_shibsp_req_";
     
     /** Class logger. */
     @Nonnull private final Logger log = LoggerFactory.getLogger(ProcessCorrelationCookie.class);
diff --git a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/ProcessEndUserClaims.java b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/ProcessEndUserClaims.java
index 6fa289e..b616588 100644
--- a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/ProcessEndUserClaims.java
+++ b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/ProcessEndUserClaims.java
@@ -38,7 +38,9 @@ import net.shibboleth.idp.profile.AbstractProfileAction;
 import net.shibboleth.oidc.profile.context.AccessTokenResponseContext;
 import net.shibboleth.oidc.profile.context.EndUserClaimsContext;
 import net.shibboleth.oidc.profile.context.UserInfoResponseContext;
+import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
 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;
 
@@ -73,14 +75,14 @@ public class ProcessEndUserClaims extends AbstractProfileAction {
             endUserClaimsContextLookupStrategy;
     
     /** The strategy used to merge UserInfo claims with id_token claims.*/
-    @Nonnull private BinaryOperator<ClaimsSet> claimMergingStrategy;
+    @NonnullAfterInit private BinaryOperator<ClaimsSet> claimMergingStrategy;
     
     /** 
      * The strategy used to sanitize claims in an input claimset. By default, produces a set of
      * claims without the validation claims (e.g. nonce, exp), but leaving the identity, authorization and 
      * misc claims.
      */
-    @Nonnull private UnaryOperator<ClaimsSet> claimSanitizationStrategy;
+    @NonnullAfterInit private UnaryOperator<ClaimsSet> claimSanitizationStrategy;
     
     /** The stashed UserInfo claims. This is allowably {@literal empty} if the UserInfo endpoint was not used.*/
     @NonnullBeforeExec private ClaimsSet userInfoClaims; 
@@ -105,9 +107,19 @@ public class ProcessEndUserClaims extends AbstractProfileAction {
         // Will create context. prc -> EndUserClaimsContext
         endUserClaimsContextLookupStrategy = 
                 new ChildContextLookup<>(EndUserClaimsContext.class, true);
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    protected void doInitialize() throws ComponentInitializationException {
+        super.doInitialize();
         
-        claimMergingStrategy = new DefaultClaimMergingStrategy();
-        claimSanitizationStrategy = new DefaultClaimSanitizationStrategy();
+        if (claimMergingStrategy == null) {
+            throw new ComponentInitializationException("ClaimMergingStrategy cannot be null");
+        }
+        if (claimSanitizationStrategy == null) {
+            throw new ComponentInitializationException("ClaimSanitizationStrategy cannot be null");
+        }
     }
    
     
@@ -261,7 +273,7 @@ public class ProcessEndUserClaims extends AbstractProfileAction {
  // Checkstyle: CyclomaticComplexity ON
     
     @Override protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) { 
-        
+          checkComponentActive();
           if (log.isTraceEnabled()) {
               log.trace("{} Processing {} claims", getLogPrefix(),
                       userInfoClaims.toJSONObject().size() > 0 ? "UserInfo and ID Token" : "ID Token");
diff --git a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/RequestedACRClaimsFromStateLookupStrategy.java b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/RequestedACRClaimsFromStateLookupStrategy.java
new file mode 100644
index 0000000..bbf1b53
--- /dev/null
+++ b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/RequestedACRClaimsFromStateLookupStrategy.java
@@ -0,0 +1,80 @@
+/*
+ * 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 java.util.List;
+import java.util.Objects;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import javax.annotation.Nonnull;
+import javax.annotation.concurrent.ThreadSafe;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import com.nimbusds.jwt.JWTClaimsSet;
+
+import net.shibboleth.shared.annotation.ParameterName;
+import net.shibboleth.shared.collection.CollectionSupport;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.sp.oidc.context.AuthnRequestStateDataContext;
+import net.shibboleth.sp.oidc.profile.AuthenticationRequestStateData;
+
+/**
+ * Strategy that looks up any requested ACR claims from the saved off authentication request state. Not judgement is
+ * made over how these ACR values were requested, for example, either as essential or optional.
+ */
+ at ThreadSafe
+public class RequestedACRClaimsFromStateLookupStrategy 
+            implements BiFunction<ProfileRequestContext,JWTClaimsSet, List<String>> {
+    
+    /** 
+     * Strategy used to locate the {@link AuthnRequestStateDataContext}.
+     */
+    @Nonnull private final Function<ProfileRequestContext, AuthnRequestStateDataContext> authenticationRequestLookupStrategy;
+
+    /**
+     * Constructor.
+     *
+     * @param strategy strategy to find the authentication request state context
+     */
+    public RequestedACRClaimsFromStateLookupStrategy(@ParameterName(name = "authenticationRequestLookupStrategy")
+            @Nonnull final Function<ProfileRequestContext, AuthnRequestStateDataContext> strategy) {
+        authenticationRequestLookupStrategy = Constraint.isNotNull(strategy,
+                "authenticationRequestLookupStrategy can not be null");
+    }
+    
+
+    @Override
+    @Nonnull public List<String> apply(final ProfileRequestContext prc, final JWTClaimsSet claims) {
+        
+        final AuthnRequestStateDataContext authnRequestCtx = authenticationRequestLookupStrategy.apply(prc);
+        
+        if (authnRequestCtx == null) {
+            return CollectionSupport.emptyList();
+        }
+        final AuthenticationRequestStateData authnRequest = authnRequestCtx.getAuthnState();
+        if (authnRequest == null) {
+            return CollectionSupport.emptyList();
+        }
+        return authnRequest.getAcrs().stream()
+                .filter(Objects::nonNull)
+                .map(String::trim)
+                .filter(s -> !s.isEmpty()).collect(CollectionSupport.nonnullCollector(Collectors.toList())).get();
+
+    }
+
+}
diff --git a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/RequestedACRValidationActivationCondition.java b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/RequestedACRValidationActivationCondition.java
new file mode 100644
index 0000000..01234b6
--- /dev/null
+++ b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/RequestedACRValidationActivationCondition.java
@@ -0,0 +1,83 @@
+/*
+ * 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 java.util.function.BiPredicate;
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.ThreadSafe;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
+
+import com.nimbusds.jwt.JWTClaimsSet;
+
+import net.shibboleth.oidc.profile.config.OIDCSSORelyingPartyConfiguration;
+import net.shibboleth.profile.context.RelyingPartyContext;
+import net.shibboleth.shared.annotation.ParameterName;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.primitive.LoggerFactory;
+
+/**
+ * An activation condition that activates if the profile configuration indicates that ACR value in the id_token
+ * should be validated against those in the request.
+ * 
+ * <p>If, for some reason, the profile configuration cannot be determined, the condition evaluates to true. That is,
+ * ACR validation will be performed.</p>
+ */
+ at ThreadSafe
+public class RequestedACRValidationActivationCondition implements BiPredicate<ProfileRequestContext, JWTClaimsSet> {
+    
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(RequestedACRValidationActivationCondition.class);
+    
+    /** Lookup function for relying party context. */
+    @Nonnull private final Function<ProfileRequestContext,RelyingPartyContext> relyingPartyContextLookupStrategy;
+    
+    /**
+     * Constructor.
+     *
+     * @param strategy the strategy to find the relying party context
+     */
+    public RequestedACRValidationActivationCondition(@ParameterName(name="relyingPartyContextLookupStrategy")
+            @Nonnull final Function<ProfileRequestContext,RelyingPartyContext> strategy) {
+        relyingPartyContextLookupStrategy = 
+                Constraint.isNotNull(strategy, "Relying Party Context lookup strategy can not be null");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean test(@Nullable final ProfileRequestContext prc, @Nullable final JWTClaimsSet claims) {
+        if (prc == null) {
+            // Activate if we can not determine from the profile configuration what to do
+            log.debug("No ProfileRequestContext available, activating ACR validation");
+            return true;
+        }
+        
+        final RelyingPartyContext rpCtx = relyingPartyContextLookupStrategy.apply(prc);     
+        OIDCSSORelyingPartyConfiguration profileConfiguration = null;
+        if (rpCtx != null && rpCtx.getProfileConfig() instanceof final OIDCSSORelyingPartyConfiguration rpConfig) {
+            profileConfiguration = rpConfig;
+        }
+        if (profileConfiguration == null) {
+            // In the absence of a profile configuration option, we activate this validator
+            log.debug("No profileConfiguration available, activating ACR validation");
+            return true;
+        }
+        return profileConfiguration.shouldValidateAcrValue(prc);
+    }
+}
diff --git a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/DefaultClaimSanitizationStrategy.java b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/ValidationClaimStrippingSanitizationStrategy.java
similarity index 87%
rename from sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/DefaultClaimSanitizationStrategy.java
rename to sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/ValidationClaimStrippingSanitizationStrategy.java
index 3991bfb..3347c8a 100644
--- a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/DefaultClaimSanitizationStrategy.java
+++ b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/ValidationClaimStrippingSanitizationStrategy.java
@@ -31,19 +31,20 @@ import net.shibboleth.oidc.security.jwt.claims.impl.JWTClaims;
  * Produce a claims set from the JWT claims set without either the validation claims or claims with null values.
  * Leaving the identity, authorization, and misc. claims.
  * 
+ * <p>Noting this strategy is less restrictive over what is filters for the OIDC SP than it is for the 
+ * OIDC-Proxy case, as more information could transit from the Hub back to the agent.</p>
+ * 
  */
-public class DefaultClaimSanitizationStrategy implements UnaryOperator<ClaimsSet> {
+public class ValidationClaimStrippingSanitizationStrategy implements UnaryOperator<ClaimsSet> {
     
     /** The set of validation claims to filter out of the input claims.*/
     @Nonnull private final Set<String> validationClaims;
     
     /** Constructor.*/
-    public DefaultClaimSanitizationStrategy() {
+    public ValidationClaimStrippingSanitizationStrategy() {
         final Set<String> validationClaimsBuilt = Set.of(IDTokenClaims.AUTHORIZED_PARTY.getClaimName(),
                 IDTokenClaims.NONCE.getClaimName(),
                 IDTokenClaims.AUTHENTICATION_TIME.getClaimName(),
-                IDTokenClaims.AUTHENTICATION_CONTEXT_CLASS_REFERENCE.getClaimName(),
-                IDTokenClaims.AUTHENTICATION_METHODS_REFERENCES.getClaimName(),
                 JWTClaims.ISSUER_CLAIM.getClaimName(),
                 JWTClaims.ISSUED_AT_CLAIM.getClaimName(),
                 JWTClaims.AUDIENCE_CLAIM.getClaimName(),
diff --git a/sp-oidc-impl/src/test/java/net/shibboleth/sp/oidc/profile/impl/RequestedACRClaimsFromStateLookupStrategyTest.java b/sp-oidc-impl/src/test/java/net/shibboleth/sp/oidc/profile/impl/RequestedACRClaimsFromStateLookupStrategyTest.java
new file mode 100644
index 0000000..18facca
--- /dev/null
+++ b/sp-oidc-impl/src/test/java/net/shibboleth/sp/oidc/profile/impl/RequestedACRClaimsFromStateLookupStrategyTest.java
@@ -0,0 +1,97 @@
+/*
+ * 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.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.mockito.Mockito;
+import org.testng.annotations.Test;
+
+import net.shibboleth.shared.collection.CollectionSupport;
+import net.shibboleth.sp.oidc.context.AuthnRequestStateDataContext;
+import net.shibboleth.sp.oidc.profile.AuthenticationRequestStateData;
+import net.shibboleth.sp.profile.impl.BaseAgplicationActionTest;
+
+/**
+ * Tests for {@link RequestedACRClaimsFromStateLookupStrategy}.
+ */
+public class RequestedACRClaimsFromStateLookupStrategyTest extends BaseAgplicationActionTest {
+
+    @Test
+    public void testReturnsEmpty_AuthnRequestContextNull() {
+        final var strategy = new RequestedACRClaimsFromStateLookupStrategy(prc -> null);
+
+        final List<String> result = strategy.apply(prc, null);
+        assertNotNull(result);
+    }
+
+    @Test
+    public void testSuccess_AcrReturnedNullRemoved() {
+        
+        // Arrange
+        final AuthnRequestStateDataContext ctx = Mockito.mock(AuthnRequestStateDataContext.class);
+        final AuthenticationRequestStateData state = Mockito.mock(AuthenticationRequestStateData.class);
+
+        Mockito.when(ctx.getAuthnState()).thenReturn(state);
+        Mockito.when(state.getAcrs()).thenReturn(Arrays.asList("loa1", null));
+        
+        final var strategy = new RequestedACRClaimsFromStateLookupStrategy(prc -> ctx);
+
+        final List<String> result = strategy.apply(prc,null);
+        assertNotNull(result);
+        // Expect trimming and filtering 
+        assertEquals(result, Arrays.asList("loa1"));
+    }
+    
+    @Test
+    public void testSuccess_EmptyAcrList() {
+        
+        // Arrange
+        final AuthnRequestStateDataContext ctx = Mockito.mock(AuthnRequestStateDataContext.class);
+        final AuthenticationRequestStateData state = Mockito.mock(AuthenticationRequestStateData.class);
+
+        Mockito.when(ctx.getAuthnState()).thenReturn(state);
+        Mockito.when(state.getAcrs()).thenReturn(CollectionSupport.emptyList());
+        
+        final var strategy = new RequestedACRClaimsFromStateLookupStrategy(prc -> ctx);
+
+        final List<String> result = strategy.apply(prc,null);
+        assertNotNull(result);
+        // Expect trimming and filtering 
+        assertTrue(result.isEmpty());
+    }
+    
+    @Test
+    public void testSuccess_NullRequestState() {
+        
+        // Arrange
+        final AuthnRequestStateDataContext ctx = Mockito.mock(AuthnRequestStateDataContext.class);
+
+        Mockito.when(ctx.getAuthnState()).thenReturn(null);
+        
+        final var strategy = new RequestedACRClaimsFromStateLookupStrategy(prc -> ctx);
+
+        final List<String> result = strategy.apply(prc,null);
+        assertNotNull(result);
+        // Expect trimming and filtering 
+        assertTrue(result.isEmpty());
+    }
+
+}
diff --git a/sp-oidc-impl/src/test/java/net/shibboleth/sp/oidc/profile/impl/RequestedACRValidationActivationConditionTest.java b/sp-oidc-impl/src/test/java/net/shibboleth/sp/oidc/profile/impl/RequestedACRValidationActivationConditionTest.java
new file mode 100644
index 0000000..600491c
--- /dev/null
+++ b/sp-oidc-impl/src/test/java/net/shibboleth/sp/oidc/profile/impl/RequestedACRValidationActivationConditionTest.java
@@ -0,0 +1,79 @@
+/*
+ * 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.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import net.shibboleth.oidc.profile.config.impl.DefaultOIDCAuthorizationConfiguration;
+import net.shibboleth.profile.context.RelyingPartyContext;
+import net.shibboleth.shared.component.ComponentInitializationException;
+import net.shibboleth.sp.profile.impl.BaseAgplicationActionTest;
+
+/**
+ * Tests for {@link RequestedACRValidationActivationCondition}
+ */
+public class RequestedACRValidationActivationConditionTest extends BaseAgplicationActionTest {
+    
+    private RequestedACRValidationActivationCondition condition;    
+    private RelyingPartyContext partyContext;    
+    protected DefaultOIDCAuthorizationConfiguration rpConfig;
+    
+    @Override
+    @BeforeMethod
+    public void beforeMethod() throws ComponentInitializationException {
+        super.beforeMethod();
+                
+        partyContext = prc.ensureSubcontext(RelyingPartyContext.class);
+        rpConfig = new DefaultOIDCAuthorizationConfiguration();
+        partyContext.setProfileConfig(rpConfig);
+        prc.addSubcontext(partyContext);
+        
+        condition = new RequestedACRValidationActivationCondition(prc-> partyContext);
+
+    }
+    
+    
+    @Test
+    public void testShouldValidateAcr_fromProfileConfig() {        
+        rpConfig.setShouldValidateAcrValue(true);
+        final boolean activate = condition.test(prc, null);
+        assertTrue(activate);
+    }
+    
+    @Test
+    public void testShouldNotValidateAcr_fromProfileConfig() {        
+        rpConfig.setShouldValidateAcrValue(false);
+        final boolean activate = condition.test(prc, null);
+        assertFalse(activate);
+    }
+    
+    @Test
+    public void testShouldValidateAcr_NoProfileConfig() {        
+        condition = new RequestedACRValidationActivationCondition(prc-> null);
+        final boolean activate = condition.test(prc, null);
+        assertTrue(activate);
+    }
+    
+    @Test
+    public void testShouldValidateAcr_NoProfileRequestContext() {        
+        final boolean activate = condition.test(null, null);
+        assertTrue(activate);
+    }
+
+}

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


More information about the commits mailing list