[java-idp-oidc] branch main updated: JOIDC-104 - Support to manipulate claims within the ID_Token

Henri Mikkonen henri.mikkonen at iki.fi
Fri Jun 10 07:00:56 UTC 2022


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=ee177c7d13f3f78920e2972cc6e2782bda34b006

The following commit(s) were added to refs/heads/main by this push:
     new ee177c7d JOIDC-104 - Support to manipulate claims within the ID_Token
ee177c7d is described below

commit ee177c7d13f3f78920e2972cc6e2782bda34b006
Author: Henri Mikkonen <henri.mikkonen at iki.fi>
AuthorDate: Fri Jun 10 10:00:28 2022 +0300

    JOIDC-104 - Support to manipulate claims within the ID_Token
    
    https://shibboleth.atlassian.net/browse/JOIDC-104
    
    Improved unit and flow testing.
---
 .../oidc/op/profile/flow/AuthorizeFlowTest.java    |  9 ++-
 .../plugin/oidc/op/profile/flow/TokenFlowTest.java |  6 ++
 .../impl/ManipulateClaimsForIDTokenTest.java       | 81 +++++++++++++++++++++-
 .../src/test/resources/conf/global.xml             | 12 ++++
 .../src/test/resources/conf/relying-party.xml      |  4 +-
 5 files changed, 106 insertions(+), 6 deletions(-)

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 575adcfd..fc3fe75e 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
@@ -46,6 +46,7 @@ import com.nimbusds.openid.connect.sdk.AuthenticationResponse;
 import com.nimbusds.openid.connect.sdk.AuthenticationSuccessResponse;
 import com.nimbusds.openid.connect.sdk.claims.ClaimRequirement;
 import com.nimbusds.openid.connect.sdk.claims.ClaimsSetRequest;
+import com.nimbusds.openid.connect.sdk.claims.IDTokenClaimsSet;
 
 import net.shibboleth.idp.plugin.oidc.op.token.support.AccessTokenClaimsSet;
 import net.shibboleth.idp.plugin.oidc.op.token.support.AuthorizeCodeClaimsSet;
@@ -467,7 +468,8 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     }
 
     @Test
-    public void testWithHybridIdTokenTokenFlowWithCustomTokenClaim() throws IOException, SessionException, ParseException, DataSealerException {
+    public void testWithHybridIdTokenTokenFlowWithCustomTokenClaim() throws IOException, SessionException,
+        ParseException, DataSealerException, com.nimbusds.oauth2.sdk.ParseException {
         request.setMethod("GET");
         request.setQueryString("client_id=mockClientIdCustomTokens&response_type=code+id_token+token&scope=openid%20profile"
                 + "&redirect_uri="+ redirectUri + "&nonce=idhas3h23hi13h1o2i32");
@@ -484,6 +486,11 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
         Assert.assertNotNull(successResponse.getAuthorizationCode());
         Assert.assertNull(successResponse.getIssuer());
 
+        final IDTokenClaimsSet idToken = new IDTokenClaimsSet(successResponse.getIDToken().getJWTClaimsSet());
+        Assert.assertNotNull(idToken);
+        Assert.assertNotNull(idToken.getStringClaim("custom_id_token_claim"));
+        Assert.assertEquals(idToken.getStringClaim("custom_id_token_claim"), "value1");
+
         final AccessTokenClaimsSet token =
                 AccessTokenClaimsSet.parse(successResponse.getAccessToken().getValue(), getDataSealer());
         Assert.assertEquals(token.getAudience(), Collections.singletonList(issuer));
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/TokenFlowTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/TokenFlowTest.java
index 8aa9ace5..12d7ecc4 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/TokenFlowTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/TokenFlowTest.java
@@ -50,6 +50,7 @@ import com.nimbusds.oauth2.sdk.token.AccessToken;
 import com.nimbusds.oauth2.sdk.token.RefreshToken;
 import com.nimbusds.openid.connect.sdk.OIDCTokenResponse;
 import com.nimbusds.openid.connect.sdk.claims.ClaimsSet;
+import com.nimbusds.openid.connect.sdk.claims.IDTokenClaimsSet;
 
 import net.minidev.json.JSONObject;
 import net.shibboleth.idp.plugin.oidc.op.profile.impl.ValidateGrantTest;
@@ -201,6 +202,11 @@ public class TokenFlowTest extends AbstractOidcClientAuthenticationFlowTest {
         Assert.assertNotNull(response.getTokens().getRefreshToken());
         Assert.assertNotNull(response.getOIDCTokens().getIDToken());
 
+        final IDTokenClaimsSet idToken = new IDTokenClaimsSet(response.getOIDCTokens().getIDToken().getJWTClaimsSet());
+        Assert.assertNotNull(idToken);
+        Assert.assertNotNull(idToken.getStringClaim("custom_id_token_claim"));
+        Assert.assertEquals(idToken.getStringClaim("custom_id_token_claim"), "value1");
+
         final AccessTokenClaimsSet accessToken =
                 AccessTokenClaimsSet.parse(response.getTokens().getAccessToken().getValue(), getDataSealer());
         final String atCustomAtClaim = accessToken.getClaimsSet().getStringClaim("custom_access_token_claim");
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ManipulateClaimsForIDTokenTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ManipulateClaimsForIDTokenTest.java
index 0ef45578..5dd088bb 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ManipulateClaimsForIDTokenTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ManipulateClaimsForIDTokenTest.java
@@ -36,6 +36,7 @@ import org.testng.annotations.Test;
 
 import com.nimbusds.oauth2.sdk.ParseException;
 import com.nimbusds.oauth2.sdk.id.Audience;
+import com.nimbusds.openid.connect.sdk.claims.AMR;
 import com.nimbusds.openid.connect.sdk.claims.IDTokenClaimsSet;
 
 /** {@link ManipulateClaimsForIDToken} unit test. */
@@ -63,24 +64,87 @@ public class ManipulateClaimsForIDTokenTest extends BaseOIDCResponseActionTest {
         ActionTestingSupport.assertEvent(event, EventIds.INVALID_MSG_CTX);
 
     }
-    
+
+    @Test
+    public void testSuccessSameMapDoesntChangeContents() throws ComponentInitializationException, ParseException {
+        final Instant now = Instant.now();
+        setIdTokenToResponseContext("iss", "sub", "aud", now, now);
+        Assert.assertEquals(respCtx.getIDToken().toJSONObject().size(), 5);
+        init((prc, map) -> map);
+        final Event event = action.execute(requestCtx);
+        ActionTestingSupport.assertProceedEvent(event);
+        final IDTokenClaimsSet idToken = respCtx.getIDToken();
+        Assert.assertEquals(idToken.toJSONObject().size(), 5);
+        Assert.assertEquals(idToken.getSubject().getValue(), "sub");
+        Assert.assertEquals(idToken.getIssuer().getValue(), "iss");
+        Assert.assertEquals(idToken.getAudience(), Audience.create("aud"));
+        Assert.assertEquals(idToken.getIssueTime().toInstant().getEpochSecond(), now.getEpochSecond());
+        Assert.assertEquals(idToken.getExpirationTime().toInstant().getEpochSecond(), now.getEpochSecond());
+    }
+
     @Test
     public void testSuccessManipulationForIss() throws ComponentInitializationException, ParseException {
         final Instant now = Instant.now();
         setIdTokenToResponseContext("iss", "sub", "aud", now, now);
+        Assert.assertEquals(respCtx.getIDToken().toJSONObject().size(), 5);
         final Map<String, Object> manipulatedClaims = Map.of("iss", "manipulatedIss", "sub", "sub", "aud",
                 List.of("aud"), "exp", now.getEpochSecond(), "iat", now.getEpochSecond());
         init(mockFunction(manipulatedClaims));
         final Event event = action.execute(requestCtx);
         ActionTestingSupport.assertProceedEvent(event);
         final IDTokenClaimsSet idToken = respCtx.getIDToken();
+        Assert.assertEquals(idToken.toJSONObject().size(), 5);
         Assert.assertEquals(idToken.getSubject().getValue(), "sub");
         Assert.assertEquals(idToken.getIssuer().getValue(), "manipulatedIss");
         Assert.assertEquals(idToken.getAudience(), Audience.create("aud"));
         Assert.assertEquals(idToken.getIssueTime().toInstant().getEpochSecond(), now.getEpochSecond());
         Assert.assertEquals(idToken.getExpirationTime().toInstant().getEpochSecond(), now.getEpochSecond());
     }
-    
+
+    @Test
+    public void testSuccessManipulationAddStandardClaims() throws ComponentInitializationException, ParseException {
+        final Instant now = Instant.now();
+        setIdTokenToResponseContext("iss", "sub", "aud", now, now);
+        Assert.assertEquals(respCtx.getIDToken().toJSONObject().size(), 5);
+        final Map<String, Object> manipulatedClaims = Map.of("iss", "manipulatedIss", "sub", "sub", "aud",
+                List.of("aud"), "exp", now.getEpochSecond(), "iat", now.getEpochSecond(), "acr", "password",
+                "amr", List.of("face"));
+        init(mockFunction(manipulatedClaims));
+        final Event event = action.execute(requestCtx);
+        ActionTestingSupport.assertProceedEvent(event);
+        final IDTokenClaimsSet idToken = respCtx.getIDToken();
+        Assert.assertEquals(idToken.toJSONObject().size(), 7);
+        Assert.assertEquals(idToken.getSubject().getValue(), "sub");
+        Assert.assertEquals(idToken.getIssuer().getValue(), "manipulatedIss");
+        Assert.assertEquals(idToken.getAudience(), Audience.create("aud"));
+        Assert.assertEquals(idToken.getIssueTime().toInstant().getEpochSecond(), now.getEpochSecond());
+        Assert.assertEquals(idToken.getExpirationTime().toInstant().getEpochSecond(), now.getEpochSecond());
+        Assert.assertEquals(idToken.getACR().getValue(), "password");
+        Assert.assertEquals(idToken.getAMR(), List.of(AMR.FACE));
+    }
+
+    @Test
+    public void testSuccessManipulationAddCustomClaims() throws ComponentInitializationException, ParseException {
+        final Instant now = Instant.now();
+        setIdTokenToResponseContext("iss", "sub", "aud", now, now);
+        Assert.assertEquals(respCtx.getIDToken().toJSONObject().size(), 5);
+        final Map<String, Object> manipulatedClaims = Map.of("iss", "manipulatedIss", "sub", "sub", "aud",
+                List.of("aud"), "exp", now.getEpochSecond(), "iat", now.getEpochSecond(), "custom1", 3,
+                "custom2", List.of("customV1", "customV2"));
+        init(mockFunction(manipulatedClaims));
+        final Event event = action.execute(requestCtx);
+        ActionTestingSupport.assertProceedEvent(event);
+        final IDTokenClaimsSet idToken = respCtx.getIDToken();
+        Assert.assertEquals(idToken.toJSONObject().size(), 7);
+        Assert.assertEquals(idToken.getSubject().getValue(), "sub");
+        Assert.assertEquals(idToken.getIssuer().getValue(), "manipulatedIss");
+        Assert.assertEquals(idToken.getAudience(), Audience.create("aud"));
+        Assert.assertEquals(idToken.getIssueTime().toInstant().getEpochSecond(), now.getEpochSecond());
+        Assert.assertEquals(idToken.getExpirationTime().toInstant().getEpochSecond(), now.getEpochSecond());
+        Assert.assertEquals(idToken.getNumberClaim("custom1").toString(), Integer.valueOf(3).toString());
+        Assert.assertEquals(idToken.getStringListClaim("custom2"), List.of("customV1", "customV2"));
+    }
+
     @Test
     public void testFailedManipulationDueMissingIss() throws ComponentInitializationException, ParseException {
         final Instant now = Instant.now();
@@ -91,7 +155,18 @@ public class ManipulateClaimsForIDTokenTest extends BaseOIDCResponseActionTest {
         final Event event = action.execute(requestCtx);
         ActionTestingSupport.assertEvent(event, IdPEventIds.INVALID_PROFILE_CONFIG);
     }    
-    
+
+    @Test
+    public void testFailedManipulationDueInvalidIatFormat() throws ComponentInitializationException, ParseException {
+        final Instant now = Instant.now();
+        setIdTokenToResponseContext("iss", "sub", "aud", now, now);
+        final Map<String, Object> manipulatedClaims = Map.of("iss", "iss", "sub", "sub", "aud",
+                List.of("aud"), "exp", now.getEpochSecond(), "iat", "invalidFormat");
+        init(mockFunction(manipulatedClaims));
+        final Event event = action.execute(requestCtx);
+        ActionTestingSupport.assertEvent(event, IdPEventIds.INVALID_PROFILE_CONFIG);
+    }    
+
     @SuppressWarnings({"rawtypes", "unchecked"})
     protected BiFunction<ProfileRequestContext,Map<String,Object>,Map<String,Object>> mockFunction(
             final Map<String, Object> result) {
diff --git a/idp-oidc-extension-impl/src/test/resources/conf/global.xml b/idp-oidc-extension-impl/src/test/resources/conf/global.xml
index 74c52462..925dd47f 100644
--- a/idp-oidc-extension-impl/src/test/resources/conf/global.xml
+++ b/idp-oidc-extension-impl/src/test/resources/conf/global.xml
@@ -39,6 +39,18 @@
     <bean id="testbed.DefaultRSSigningCredential" parent="shibboleth.JWKCredential"
         p:resource="%{idp.signing.oidc.rs.key}" />
 
+    <bean id="testIDTokenManipulation" parent="shibboleth.BiFunctions.Scripted" factory-method="inlineScript">
+        <constructor-arg name="scriptSource">
+            <value>
+             <![CDATA[
+                newMap = input2;
+                newMap.put("custom_id_token_claim", "value1");
+                newMap;
+             ]]>
+            </value>
+        </constructor-arg>
+    </bean>
+
     <bean id="testAuthorizationCodeManipulation" parent="shibboleth.BiFunctions.Scripted" factory-method="inlineScript">
         <constructor-arg name="scriptSource">
             <value>
diff --git a/idp-oidc-extension-impl/src/test/resources/conf/relying-party.xml b/idp-oidc-extension-impl/src/test/resources/conf/relying-party.xml
index 68e843f0..9e7dfb46 100644
--- a/idp-oidc-extension-impl/src/test/resources/conf/relying-party.xml
+++ b/idp-oidc-extension-impl/src/test/resources/conf/relying-party.xml
@@ -70,8 +70,8 @@
         <bean parent="RelyingPartyByName" c:relyingPartyIds="mockClientIdCustomTokens">
             <property name="profileConfigurations">
                  <list>
-                     <bean parent="OIDC.SSO.MDDriven" p:authorizationCodeClaimsSetManipulationStrategy-ref="testAuthorizationCodeManipulation" p:accessTokenClaimsSetManipulationStrategy-ref="testAccessTokenManipulation" />
-                     <bean parent="OAUTH2.Token.MDDriven" p:forcePKCE="false" p:allowPKCEPlain="true" p:accessTokenClaimsSetManipulationStrategy-ref="testAccessTokenManipulation" p:refreshTokenClaimsSetManipulationStrategy-ref="testRefreshTokenManipulation"/>
+                     <bean parent="OIDC.SSO.MDDriven" p:IDTokenManipulationStrategy-ref="testIDTokenManipulation" p:authorizationCodeClaimsSetManipulationStrategy-ref="testAuthorizationCodeManipulation" p:accessTokenClaimsSetManipulationStrategy-ref="testAccessTokenManipulation" />
+                     <bean parent="OAUTH2.Token.MDDriven" p:forcePKCE="false" p:allowPKCEPlain="true" p:IDTokenManipulationStrategy-ref="testIDTokenManipulation" p:accessTokenClaimsSetManipulationStrategy-ref="testAccessTokenManipulation" p:refreshTokenClaimsSetManipulationStrategy-ref="testRefreshTokenManipulation"/>
                  </list>
             </property>
         </bean>

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


More information about the commits mailing list