[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