[java-idp-oidc] branch main updated: JOIDC-246 - Support disallowedFeatures property for blocking essential acr requests
Henri Mikkonen
henri.mikkonen at iki.fi
Thu Jun 12 07:33:05 UTC 2025
This is an automated email from the git hooks/post-receive script.
hjmikkon pushed a commit to branch main
in repository java-idp-oidc.
View the commit online:
http://git.shibboleth.net/view/?p=java-idp-oidc.git;a=commit;h=585ae9dc31dbebdbd22c84c4c9fbf71557a4485c
The following commit(s) were added to refs/heads/main by this push:
new 585ae9dc JOIDC-246 - Support disallowedFeatures property for blocking essential acr requests
585ae9dc is described below
commit 585ae9dc31dbebdbd22c84c4c9fbf71557a4485c
Author: Henri Mikkonen <henri.mikkonen at iki.fi>
AuthorDate: Thu Jun 12 10:32:44 2025 +0300
JOIDC-246 - Support disallowedFeatures property for blocking essential acr requests
https://shibboleth.atlassian.net/browse/JOIDC-246
- Add OIDC.SSO.FEATURE_ESSENTIAL_ACR_REQUEST bean to help relying party configuration
- Add flow test to verify that the essential ACR request is blocked when the bean is used
- <bean parent="OIDC.SSO" p:disallowedFeatures-ref="OIDC.SSO.FEATURE_ESSENTIAL_ACR_REQUEST"/>
---
.../idp/service/relying-party/postconfig.xml | 5 +-
.../oidc/op/profile/flow/AuthorizeFlowTest.java | 94 +++++++++++++++++++++-
.../net/shibboleth/idp/module/conf/oidc.properties | 8 +-
.../shibboleth/idp/module/conf/relying-party.xml | 7 ++
4 files changed, 110 insertions(+), 4 deletions(-)
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml
index ff57e024..51e40013 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml
@@ -870,6 +870,9 @@
p:objectMapper-ref="shibboleth.oidc.JSONObjectMapper"
p:nonceLifetime="%{idp.oauth2.dpop.nonceLifetime:PT5M}"
c:sealer-ref="DefaultDPoPNonceSealer">
-
</bean>
+
+ <util:constant id="OIDC.SSO.FEATURE_ESSENTIAL_ACR_REQUEST"
+ static-field="net.shibboleth.oidc.profile.config.OIDCAuthenticationProfileConfiguration.FEATURE_ESSENTIAL_ACR_REQUEST"/>
+
</beans>
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/AuthorizeFlowTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/AuthorizeFlowTest.java
index 12a978a8..2bbd6b4b 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/AuthorizeFlowTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/AuthorizeFlowTest.java
@@ -2278,6 +2278,40 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
Assert.assertEquals(getJktFromAuthorizeCodeClaimsSet(successResponse), "mockDPoPJktValue");
}
+ @Test
+ public void testWithPlainReqObjectVoluntaryAcrRequest() throws IOException, SessionException,
+ DataSealerException, ParseException {
+ final JWTClaimsSet ro = JWTClaimsSet.parse(getRequestObjectWithAcrRequestPayload(clientId, false));
+ assertSuccessRequestObjectWithAcrRequestResponse(new PlainJWT(ro).serialize(), null, false);
+ }
+
+ @Test
+ public void testWithPlainReqObjectEssentialAcrRequest() throws IOException, SessionException,
+ DataSealerException, ParseException {
+ final JWTClaimsSet ro = JWTClaimsSet.parse(getRequestObjectWithAcrRequestPayload(clientId, true));
+ assertSuccessRequestObjectWithAcrRequestResponse(new PlainJWT(ro).serialize(), null, true);
+ }
+
+ @Test
+ public void testWithPlainReqObjectEssentialAcrRequest_requestDisabled() throws IOException, SessionException,
+ DataSealerException, ParseException {
+ final String clientId = "mockClientIdEssentialAcrRequestDisabled";
+ final JWTClaimsSet ro = JWTClaimsSet.parse(getRequestObjectWithAcrRequestPayload(clientId, true));
+ request.setMethod("GET");
+ setRequestParameters(List.of(new Pair<>("client_id", clientId),
+ new Pair<>("response_type", "code"),
+ new Pair<>("scope", "openid profile"),
+ new Pair<>("redirect_uri", redirectUri),
+ new Pair<>("request", new PlainJWT(ro).serialize())));
+ storeMetadata(storageService, clientId, clientSecret, scope, redirectUri);
+
+ initializeThreadLocals();
+
+ final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+ assertErrorCode(result, "invalid_request");
+ assertErrorDescriptionContains(result, "AccessDenied");
+ }
+
@Factory
public Object[] createIdTokenSecurityTests() {
return new Object[] {
@@ -2343,6 +2377,29 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
+ "}";
}
+ protected String getRequestObjectWithAcrRequestPayload(final String clientId, final boolean essential) {
+ return "{\n"
+ + " \"iss\": \"" + clientId + "\",\n"
+ + " \"response_type\": \"code\",\n"
+ + " \"code_challenge_method\": \"S256\",\n"
+ + " \"nonce\": \"k5r-Uwjw0KKr18XiKD2VbiLtD2adwt85_HiSvzBi8FI\",\n"
+ + " \"client_id\": \"" + clientId + "\",\n"
+ + " \"aud\": \"https://op.example.org\",\n"
+ + " \"scope\": \"openid profile offline_access\",\n"
+ + " \"claims\": {\n"
+ + " \"id_token\": {\n"
+ + " \"acr\": {\n"
+ + " \"essential\": " + essential + ",\n"
+ + " \"values\": [\"password\"]"
+ + " }\n"
+ + " }\n"
+ + " },\n"
+ + " \"redirect_uri\": \"" + redirectUri + "\",\n"
+ + " \"state\": \"81c33d57-59c7-4b41-9a15-80e2ed1482e21646857349537\",\n"
+ + " \"code_challenge\": \"MiAR-UxCj6oVyPatcUnrb3MGEZbwLKBmIRSoOKLLTl0\"\n"
+ + "}";
+ }
+
protected void assertSuccessRequestObjectWithClaimsRequestResponse(final JWT requestObject)
throws ParseException, DataSealerException, IOException {
assertSuccessRequestObjectWithClaimsRequestResponse(requestObject, null);
@@ -2411,10 +2468,43 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
null);
Assert.assertEquals(givenName.getClaimName(), "given_name");
Assert.assertEquals(givenName.getClaimRequirement(), ClaimRequirement.ESSENTIAL);
+ }
-
+ protected void assertSuccessRequestObjectWithAcrRequestResponse(final String requestObject,
+ final String acr, final boolean essential) throws ParseException, DataSealerException, IOException {
+ request.setMethod("GET");
+ setRequestParameters(List.of(new Pair<>("client_id", "mockClientId"),
+ new Pair<>("response_type", "code"),
+ new Pair<>("scope", "openid profile"),
+ new Pair<>("redirect_uri", redirectUri),
+ new Pair<>("request", requestObject)));
+ storeMetadata(storageService, clientId, clientSecret, scope, redirectUri);
+
+ initializeThreadLocals();
+
+ final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+ final AuthenticationResponse responseMessage = parseSuccessResponse(result, AuthenticationResponse.class);
+ final AuthenticationSuccessResponse successResponse = responseMessage.toSuccessResponse();
+ Assert.assertEquals(successResponse.getRedirectionURI().toString(), redirectUri);
+ Assert.assertNull(successResponse.getIDToken());
+ Assert.assertNull(successResponse.getAccessToken());
+ Assert.assertNotNull(successResponse.getAuthorizationCode());
+ Assert.assertNotNull(getSidFromAuthorizeCodeClaimsSet(successResponse));
+ Assert.assertNull(successResponse.getIssuer());
+
+ final AuthorizeCodeClaimsSet code =
+ AuthorizeCodeClaimsSet.parse(successResponse.getAuthorizationCode().getValue(), getDataSealer());
+ Assert.assertNotNull(code.getClaimsRequest());
+ final OIDCClaimsRequest claimsRequest = code.getClaimsRequest();
+ assert claimsRequest != null;
+ Assert.assertNotNull(claimsRequest.getIDTokenClaimsRequest());
+ final ClaimsSetRequest.Entry acrEntry = claimsRequest.getIDTokenClaimsRequest().get("acr",
+ null);
+ Assert.assertEquals(acrEntry.getClaimName(), "acr");
+ Assert.assertEquals(acrEntry.getClaimRequirement(),
+ essential ? ClaimRequirement.ESSENTIAL : ClaimRequirement.VOLUNTARY);
}
-
+
protected void assertRequestObjectError(final JWT requestObject) throws IOException {
assertRequestObjectError(requestObject, null);
}
diff --git a/idp-oidc-extension-impl/src/test/resources/net/shibboleth/idp/module/conf/oidc.properties b/idp-oidc-extension-impl/src/test/resources/net/shibboleth/idp/module/conf/oidc.properties
index 358776e4..9e568450 100644
--- a/idp-oidc-extension-impl/src/test/resources/net/shibboleth/idp/module/conf/oidc.properties
+++ b/idp-oidc-extension-impl/src/test/resources/net/shibboleth/idp/module/conf/oidc.properties
@@ -24,4 +24,10 @@ idp.oidc.DefaultUnregisteredClientPolicyFile = src/test/resources/net/shibboleth
idp.oauth2.jwtAuth.targetedEndpointAsJWTAudience = true
idp.oauth2.revocationCondition = CustomTokenRevocationCondition
-idp.oauth2.revocationCondition.attributeId = customRevocation
\ No newline at end of file
+idp.oauth2.revocationCondition.attributeId = customRevocation
+
+idp.authn.Password.supportedPrincipals = \
+ saml2/urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport, \
+ saml2/urn:oasis:names:tc:SAML:2.0:ac:classes:Password, \
+ saml1/urn:oasis:names:tc:SAML:1.0:am:password, \
+ oidc/password
diff --git a/idp-oidc-extension-impl/src/test/resources/net/shibboleth/idp/module/conf/relying-party.xml b/idp-oidc-extension-impl/src/test/resources/net/shibboleth/idp/module/conf/relying-party.xml
index c87a9ab6..37acf8fc 100644
--- a/idp-oidc-extension-impl/src/test/resources/net/shibboleth/idp/module/conf/relying-party.xml
+++ b/idp-oidc-extension-impl/src/test/resources/net/shibboleth/idp/module/conf/relying-party.xml
@@ -76,6 +76,13 @@
</bean>
<util:list id="shibboleth.RelyingPartyOverrides">
+ <bean parent="RelyingPartyByName" c:relyingPartyIds="mockClientIdEssentialAcrRequestDisabled">
+ <property name="profileConfigurations">
+ <list>
+ <bean parent="OIDC.SSO" p:disallowedFeatures-ref="OIDC.SSO.FEATURE_ESSENTIAL_ACR_REQUEST"/>
+ </list>
+ </property>
+ </bean>
<bean parent="RelyingPartyByName" c:relyingPartyIds="mockClientIdEndpointAudienceDisabled">
<property name="profileConfigurations">
<list>
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list