[java-idp-plugin-oidc-rp] branch main updated: Add more flow tests for id_token and userinfo token claims

Phil Smart philip.smart at jisc.ac.uk
Wed Feb 15 17:34:35 UTC 2023


This is an automated email from the git hooks/post-receive script.

philsmart pushed a commit to branch main
in repository java-idp-plugin-oidc-rp.

View the commit online:
http://git.shibboleth.net/view/?p=java-idp-plugin-oidc-rp.git;a=commit;h=0cd108c91b81b6edbad41275287fbaa759f43c44

The following commit(s) were added to refs/heads/main by this push:
     new 0cd108c  Add more flow tests for id_token and userinfo token claims
0cd108c is described below

commit 0cd108c91b81b6edbad41275287fbaa759f43c44
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Wed Feb 15 17:34:30 2023 +0000

    Add more flow tests for id_token and userinfo token claims
---
 .../OIDCRPFlowFromAuthenticationResponseTest.java  | 277 ++++++++++++++++-----
 .../plugin/authn/oidc/rp/impl/TestTokenHelper.java | 144 ++++++-----
 2 files changed, 300 insertions(+), 121 deletions(-)

diff --git a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCRPFlowFromAuthenticationResponseTest.java b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCRPFlowFromAuthenticationResponseTest.java
index 0a1a43c..8ac0f58 100644
--- a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCRPFlowFromAuthenticationResponseTest.java
+++ b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCRPFlowFromAuthenticationResponseTest.java
@@ -2,6 +2,7 @@
 package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
 
 import java.net.URI;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -105,10 +106,11 @@ public class OIDCRPFlowFromAuthenticationResponseTest extends OIDCRPFlowTest {
         
         final MockWebServer mockOPServer = createSimpleServer();
         final var accessTokenResp = TestTokenHelper.createAccessTokenResponseJSON(
-                OP_ISSUER_ID, CLIENT_ID, List.of(CLIENT_ID), JWSAlgorithm.HS256, null, null, 
-                new DefaultClientSecretCredential(CLIENT_SECRET).toSigningCredential(), null);
+                Map.of("iss", OP_ISSUER_ID, "azp", CLIENT_ID, "aud", List.of(CLIENT_ID)),
+                JWSAlgorithm.HS256, null, null, new DefaultClientSecretCredential(CLIENT_SECRET).toSigningCredential(), 
+                null);
         final var userInfoResp = 
-                TestTokenHelper.createPlainUserInfoResponseString(OP_ISSUER_ID, List.of(CLIENT_ID),"jdoe");
+                TestTokenHelper.createPlainUserInfoResponseString(Map.of("iss", OP_ISSUER_ID, "aud", List.of(CLIENT_ID)));
         // First is token exchange
         queueMockServerResponse(mockOPServer, 200, accessTokenResp, "application/json");
         // Second is plain userInfo
@@ -127,43 +129,6 @@ public class OIDCRPFlowFromAuthenticationResponseTest extends OIDCRPFlowTest {
         assertEndUserClaimsVerified(nestedPrc);         
     }
     
-    /** 
-     * Test the flow from the external authorization request to the end of the flow.
-     * Using a MAC signed id_token and an invalid UserInfo JSON Response - it has no subject.
-     * 
-     * @throws Exception on error.
-     */
-    @Test 
-    public void test_IDTokenHS256_InvalidPlainUserInfo() throws Exception {
-        
-        basicSetup();
-        
-        final MockWebServer mockOPServer = createSimpleServer();
-        final var accessTokenResp = TestTokenHelper.createAccessTokenResponseJSON(
-                        OP_ISSUER_ID, CLIENT_ID, List.of(CLIENT_ID), JWSAlgorithm.HS256, null, null, 
-                        new DefaultClientSecretCredential(CLIENT_SECRET).toSigningCredential(), null);
-        final var userInfoResp = 
-                TestTokenHelper.createPlainUserInfoResponseString(OP_ISSUER_ID, List.of(CLIENT_ID), null);
-        
-        // First is token exchange
-        queueMockServerResponse(mockOPServer, 200, accessTokenResp, "application/json");
-        // Second is plain userInfo
-        queueMockServerResponse(mockOPServer, 200, userInfoResp, "application/json");
-        
-        mockOPServer.start(9918);        
-
-        final var prc = resumeBasicFlow();
-        
-        mockOPServer.shutdown();
-        
-        // Assert test conditions
-        
-        assertFlowExecutionEnded();         
-        assertIdTokenSignatureAndClaimsVerified(
-                prc.getSubcontext(AuthenticationContext.class).getSubcontext(ProfileRequestContext.class));       
-        // Should have failed to produce a result
-        assertFlowEndedInErrorConditions(prc, "InvalidUserInfoClaims");  
-    }
     
     @Test 
     public void test_IDTokenHS256_UserInfoHS256() throws Exception {
@@ -172,11 +137,12 @@ public class OIDCRPFlowFromAuthenticationResponseTest extends OIDCRPFlowTest {
         
         final MockWebServer mockOPServer = createSimpleServer();
         final var accessTokenResp = TestTokenHelper.createAccessTokenResponseJSON(
-                        OP_ISSUER_ID, CLIENT_ID, List.of(CLIENT_ID), JWSAlgorithm.HS256, null, null, 
-                        new DefaultClientSecretCredential(CLIENT_SECRET).toSigningCredential(), null);     
+                Map.of("iss", OP_ISSUER_ID, "azp", CLIENT_ID, "aud", List.of(CLIENT_ID)),
+                JWSAlgorithm.HS256, null, null, new DefaultClientSecretCredential(CLIENT_SECRET).toSigningCredential(), 
+                null);     
         final var userInfoResp = TestTokenHelper.createJWTUserInfoResponse(
-                OP_ISSUER_ID, List.of(CLIENT_ID), "jdoe", JWSAlgorithm.HS256, null, null, 
-                new DefaultClientSecretCredential(CLIENT_SECRET).toSigningCredential(), null);
+                Map.of("iss", OP_ISSUER_ID, "aud", List.of(CLIENT_ID)), JWSAlgorithm.HS256, null, 
+                null, new DefaultClientSecretCredential(CLIENT_SECRET).toSigningCredential(), null);
         
         // First is token exchange
         queueMockServerResponse(mockOPServer, 200, accessTokenResp, "application/json");
@@ -206,8 +172,8 @@ public class OIDCRPFlowFromAuthenticationResponseTest extends OIDCRPFlowTest {
         final MockWebServer mockOPServer = createSimpleServer();
      
         final var accessTokenResp = TestTokenHelper.createAccessTokenResponseJSON(
-                OP_ISSUER_ID, CLIENT_ID, List.of(CLIENT_ID), JWSAlgorithm.HS256, JWEAlgorithm.DIR, 
-                EncryptionMethod.A128CBC_HS256, 
+                Map.of("iss", OP_ISSUER_ID, "azp", CLIENT_ID, "aud", List.of(CLIENT_ID)),
+                JWSAlgorithm.HS256, JWEAlgorithm.DIR, EncryptionMethod.A128CBC_HS256, 
                 new DefaultClientSecretCredential(CLIENT_SECRET).toSigningCredential(), 
                 new DefaultClientSecretCredential(CLIENT_SECRET).toEncryptionCredential(
                         JWEAlgorithm.DIR, EncryptionMethod.A128CBC_HS256));
@@ -218,10 +184,10 @@ public class OIDCRPFlowFromAuthenticationResponseTest extends OIDCRPFlowTest {
                 .keyUse(KeyUse.ENCRYPTION)
                 .generate();
         final var userInfoResp = TestTokenHelper.createJWTUserInfoResponse(
-                OP_ISSUER_ID, List.of(CLIENT_ID), "jdoe", JWSAlgorithm.ES256, JWEAlgorithm.RSA_OAEP_256, 
+                Map.of("iss", OP_ISSUER_ID, "aud", List.of(CLIENT_ID)), JWSAlgorithm.ES256, 
+                JWEAlgorithm.RSA_OAEP_256, 
                 EncryptionMethod.A256GCM, 
-                new BasicCredential(sigKey.toPublicKey(), sigKey.toPrivateKey()), 
-                new BasicCredential(encKey.toPublicKey(), encKey.toPrivateKey()));
+                new BasicCredential(sigKey.toPublicKey(), sigKey.toPrivateKey()), new BasicCredential(encKey.toPublicKey(), encKey.toPrivateKey()));
         
         // First is token exchange
         queueMockServerResponse(mockOPServer, 200, accessTokenResp, "application/json");
@@ -306,13 +272,13 @@ public class OIDCRPFlowFromAuthenticationResponseTest extends OIDCRPFlowTest {
                 .keyUse(KeyUse.ENCRYPTION)
                 .generate();        
         final var accessTokenResp = TestTokenHelper.createAccessTokenResponseJSON(
-                OP_ISSUER_ID, CLIENT_ID, List.of(CLIENT_ID), JWSAlgorithm.HS256, JWEAlgorithm.RSA_OAEP_256, 
-                EncryptionMethod.A256GCM, 
+                Map.of("iss", OP_ISSUER_ID, "azp", CLIENT_ID, "aud", List.of(CLIENT_ID)),
+                JWSAlgorithm.HS256, JWEAlgorithm.RSA_OAEP_256, EncryptionMethod.A256GCM, 
                 new DefaultClientSecretCredential(CLIENT_SECRET).toSigningCredential(), 
                 new BasicCredential(encKey.toPublicKey(), encKey.toPrivateKey()));
 
         final var userInfoResp = 
-                TestTokenHelper.createPlainUserInfoResponseString(OP_ISSUER_ID, List.of(CLIENT_ID), "jdoe");
+                TestTokenHelper.createPlainUserInfoResponseString(Map.of("iss", OP_ISSUER_ID, "aud", List.of(CLIENT_ID)));
         
         // First is token exchange
         queueMockServerResponse(mockOPServer, 200, accessTokenResp, "application/json");
@@ -376,11 +342,12 @@ public class OIDCRPFlowFromAuthenticationResponseTest extends OIDCRPFlowTest {
         
         final MockWebServer mockOPServer = createSimpleServer();
         final var accessTokenResp = TestTokenHelper.createAccessTokenResponseJSON(
-                        OP_ISSUER_ID, CLIENT_ID, List.of(CLIENT_ID), JWSAlgorithm.HS256, null, null, 
-                        new DefaultClientSecretCredential(CLIENT_SECRET).toSigningCredential(), null);
+                Map.of("iss", OP_ISSUER_ID, "azp", CLIENT_ID, "aud", List.of(CLIENT_ID)),
+                JWSAlgorithm.HS256, null, null, new DefaultClientSecretCredential(CLIENT_SECRET).toSigningCredential(), 
+                null);
         final var userInfoResp = 
-                TestTokenHelper.createPlainJWTUserInfoResponseJSON(OP_ISSUER_ID,CLIENT_ID)
-                        .serialize();        
+                TestTokenHelper.createPlainJWTUserInfoResponseJSON(
+                        Map.of("iss", OP_ISSUER_ID, "aud", List.of(CLIENT_ID))).serialize();        
         // First is token exchange
         queueMockServerResponse(mockOPServer, 200, accessTokenResp, "application/json");
         // Second is plain userInfo (tell it is a JWT type when it is not)
@@ -404,11 +371,12 @@ public class OIDCRPFlowFromAuthenticationResponseTest extends OIDCRPFlowTest {
         
         final MockWebServer mockOPServer = createSimpleServer();
         final var accessTokenResp = TestTokenHelper.createAccessTokenResponseJSON(
-                        OP_ISSUER_ID, CLIENT_ID, List.of(CLIENT_ID), JWSAlgorithm.HS256, null, null, 
+                Map.of("iss", OP_ISSUER_ID, "azp", CLIENT_ID, "aud", List.of(CLIENT_ID)),
+                        JWSAlgorithm.HS256, null, null, 
                         new DefaultClientSecretCredential(CLIENT_SECRET).toSigningCredential(), null);
         final var userInfoResp = TestTokenHelper.createJWTUserInfoResponse(
-                OP_ISSUER_ID, List.of(CLIENT_ID), "jdoe", JWSAlgorithm.HS256, null, null, 
-                new DefaultClientSecretCredential(CLIENT_SECRET).toSigningCredential(), null);
+                Map.of("iss", OP_ISSUER_ID, "aud", List.of(CLIENT_ID)), JWSAlgorithm.HS256, null, 
+                null, new DefaultClientSecretCredential(CLIENT_SECRET).toSigningCredential(), null);
         
         // First is token exchange
         queueMockServerResponse(mockOPServer, 200, accessTokenResp, "application/json");
@@ -528,5 +496,196 @@ public class OIDCRPFlowFromAuthenticationResponseTest extends OIDCRPFlowTest {
         assertFlowEndedInErrorConditions(prc, "MessageProcessingError");    
         
     }
+    
+    /** 
+     * Test a suitable error occurs if the id_token does not contain an issuer.
+     * 
+     * @throws Exception on error.
+     */
+    @Test 
+    public void test_IDTokenHS256_NoIssuer_PlainUserInfo() throws Exception {
+        
+        basicSetup();
+        
+        final MockWebServer mockOPServer = createSimpleServer();
+
+        final var accessTokenResp = TestTokenHelper.createAccessTokenResponseJSON(Map.of("iss","", "azp", 
+                CLIENT_ID, "aud", List.of(CLIENT_ID)),
+                JWSAlgorithm.HS256, null, null, 
+                new DefaultClientSecretCredential(CLIENT_SECRET).toSigningCredential(), null);
+        final var userInfoResp = 
+                TestTokenHelper.createPlainUserInfoResponseString(Map.of("iss", OP_ISSUER_ID, "aud", List.of(CLIENT_ID)));
+        // First is token exchange
+        queueMockServerResponse(mockOPServer, 200, accessTokenResp, "application/json");
+        // Second is plain userInfo
+        queueMockServerResponse(mockOPServer, 200, userInfoResp, "application/json");
+        mockOPServer.start(9918);
+        
+        final var prc = resumeBasicFlow();        
+        mockOPServer.shutdown();        
+        
+        // Assert test conditions        
+        assertFlowEndedInErrorConditions(prc, "InvalidToken");  
+    }    
+    
+   /** 
+    * Test a suitable error occurs if the id_token does not contain a subject.
+    * 
+    * @throws Exception on error.
+    */
+   @Test 
+   public void test_IDTokenHS256_NoSub_PlainUserInfo() throws Exception {
+       
+       basicSetup();
+       
+       final MockWebServer mockOPServer = createSimpleServer();       
+       final var accessTokenResp = TestTokenHelper.createAccessTokenResponseJSON(Map.of("sub","","iss", 
+               OP_ISSUER_ID, "azp", CLIENT_ID, "aud", List.of(CLIENT_ID)),
+               JWSAlgorithm.HS256, null, null, 
+               new DefaultClientSecretCredential(CLIENT_SECRET).toSigningCredential(), null);
+       
+       final var userInfoResp = 
+               TestTokenHelper.createPlainUserInfoResponseString(Map.of("iss", OP_ISSUER_ID, "aud", List.of(CLIENT_ID)));
+       // First is token exchange
+       queueMockServerResponse(mockOPServer, 200, accessTokenResp, "application/json");
+       // Second is plain userInfo
+       queueMockServerResponse(mockOPServer, 200, userInfoResp, "application/json");
+       mockOPServer.start(9918);
+       
+       final var prc = resumeBasicFlow();        
+       mockOPServer.shutdown();        
+       
+       // Assert test conditions        
+       assertFlowEndedInErrorConditions(prc, "InvalidToken");  
+   }
+   
+   /** 
+    * Test a suitable error occurs if the id_token does not contain an audience.
+    * 
+    * @throws Exception on error.
+    */
+   @Test 
+   public void test_IDTokenHS256_NoAudience_PlainUserInfo() throws Exception {
+       
+       basicSetup();
+       
+       final MockWebServer mockOPServer = createSimpleServer();
+       final var accessTokenResp = TestTokenHelper.createAccessTokenResponseJSON(
+               Map.of("aud",List.of(""),"iss", OP_ISSUER_ID, "azp", CLIENT_ID),
+               JWSAlgorithm.HS256, null, null, 
+               new DefaultClientSecretCredential(CLIENT_SECRET).toSigningCredential(), null);
+       final var userInfoResp = 
+               TestTokenHelper.createPlainUserInfoResponseString(Map.of("iss", OP_ISSUER_ID, "aud", List.of(CLIENT_ID)));
+       // First is token exchange
+       queueMockServerResponse(mockOPServer, 200, accessTokenResp, "application/json");
+       // Second is plain userInfo
+       queueMockServerResponse(mockOPServer, 200, userInfoResp, "application/json");
+       mockOPServer.start(9918);
+       
+       final var prc = resumeBasicFlow();        
+       mockOPServer.shutdown();        
+       
+       // Assert test conditions        
+       assertFlowEndedInErrorConditions(prc, "InvalidToken");  
+   }
+   
+   /** 
+   * Test a suitable error occurs if the id_token does not contain an issued-at time.
+   * 
+   * @throws Exception on error.
+   */
+  @Test 
+  public void test_IDTokenHS256_NoIssuedAt_PlainUserInfo() throws Exception {
+      
+      basicSetup();
+      
+      final MockWebServer mockOPServer = createSimpleServer();
+      final Map<String, Object> iatOverride = new HashMap<>();
+      iatOverride.put("iat", null);
+      iatOverride.put("aud",List.of(CLIENT_ID));
+      iatOverride.put("iss", OP_ISSUER_ID);
+      iatOverride.put("azp", CLIENT_ID);
+      final var accessTokenResp = TestTokenHelper.createAccessTokenResponseJSON(iatOverride,
+              JWSAlgorithm.HS256, null, null, 
+              new DefaultClientSecretCredential(CLIENT_SECRET).toSigningCredential(), null);
+      final var userInfoResp = 
+              TestTokenHelper.createPlainUserInfoResponseString(Map.of("iss", OP_ISSUER_ID, "aud", List.of(CLIENT_ID)));
+      // First is token exchange
+      queueMockServerResponse(mockOPServer, 200, accessTokenResp, "application/json");
+      // Second is plain userInfo
+      queueMockServerResponse(mockOPServer, 200, userInfoResp, "application/json");
+      mockOPServer.start(9918);
+      
+      final var prc = resumeBasicFlow();        
+      mockOPServer.shutdown();        
+      
+      // Assert test conditions        
+      assertFlowEndedInErrorConditions(prc, "InvalidToken");  
+  }
+  
+  /** 
+   * Test a suitable error occurs if the id_token does not contain a valid nonce.
+   * 
+   * @throws Exception on error.
+   */
+  @Test 
+  public void test_IDTokenHS256_InvalidNonce_PlainUserInfo() throws Exception {
+      
+      basicSetup();
+      
+      final MockWebServer mockOPServer = createSimpleServer();
+      final var accessTokenResp = TestTokenHelper.createAccessTokenResponseJSON(Map.of("nonce", "WRONG", 
+              "iss", OP_ISSUER_ID, "azp", CLIENT_ID, "aud", List.of(CLIENT_ID)),
+              JWSAlgorithm.HS256, null, null, 
+              new DefaultClientSecretCredential(CLIENT_SECRET).toSigningCredential(), null);
+      final var userInfoResp = 
+              TestTokenHelper.createPlainUserInfoResponseString(Map.of("iss", OP_ISSUER_ID, "aud", List.of(CLIENT_ID)));
+      // First is token exchange
+      queueMockServerResponse(mockOPServer, 200, accessTokenResp, "application/json");
+      // Second is plain userInfo
+      queueMockServerResponse(mockOPServer, 200, userInfoResp, "application/json");
+      mockOPServer.start(9918);
+      
+      final var prc = resumeBasicFlow();        
+      mockOPServer.shutdown();        
+      
+      // Assert test conditions        
+      assertFlowEndedInErrorConditions(prc, "InvalidToken");  
+  }
+  
+  /** 
+   * Test the flow from the external authorization request to the end of the flow.
+   * Using a MAC signed id_token and an invalid UserInfo JSON Response - it has no subject.
+   * 
+   * @throws Exception on error.
+   */
+  @Test 
+  public void test_IDTokenHS256_InvalidPlainUserInfo_NoSub() throws Exception {
+      
+      basicSetup();
+      
+      final MockWebServer mockOPServer = createSimpleServer();
+      final var accessTokenResp = TestTokenHelper.createAccessTokenResponseJSON(Map.of("iss", OP_ISSUER_ID, "azp", 
+              CLIENT_ID, "aud", List.of(CLIENT_ID)), JWSAlgorithm.HS256, null, null, 
+                      new DefaultClientSecretCredential(CLIENT_SECRET).toSigningCredential(), null);
+      final var userInfoResp = 
+              TestTokenHelper.createPlainUserInfoResponseString(
+                      Map.of("sub","", "iss", OP_ISSUER_ID, "aud", List.of(CLIENT_ID)));
+      
+      // First is token exchange
+      queueMockServerResponse(mockOPServer, 200, accessTokenResp, "application/json");
+      // Second is plain userInfo
+      queueMockServerResponse(mockOPServer, 200, userInfoResp, "application/json");
+      
+      mockOPServer.start(9918);        
+
+      final var prc = resumeBasicFlow();
+      
+      mockOPServer.shutdown();
+      
+      // Assert test conditions   
+      // Should have failed to produce a result
+      assertFlowEndedInErrorConditions(prc, "InvalidUserInfoClaims");  
+  }
 
 }
diff --git a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/TestTokenHelper.java b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/TestTokenHelper.java
index 9a49ff8..8bc4547 100644
--- a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/TestTokenHelper.java
+++ b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/TestTokenHelper.java
@@ -25,6 +25,7 @@ import java.text.ParseException;
 import java.time.Instant;
 import java.util.Date;
 import java.util.List;
+import java.util.Map;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
@@ -66,60 +67,83 @@ public final class TestTokenHelper {
     /**
      * Build a basic {@link JWTClaimsSet} from the supplied parameters.
      * 
-     * @param opIssuerId the OP's issuer ID
-     * @param clientId the client_id
-     * @param audience the list of audiences
+     * @param overrideClaims claims to override
      * 
      * @return the JWT claims set.
      */
-    public static JWTClaimsSet createBasicClaims(
-            final String opIssuerId, final String clientId, 
-            final List<String> audience) {
+    public static JWTClaimsSet createBasicClaims(final Map<String, Object> overrideClaims) {
         
         return new JWTClaimsSet.Builder()
-                .issuer(opIssuerId)
-                .audience(audience)
-                .subject("jdoe")
-                .claim("preferred_username", "d.tu")
-                .claim("given_name", "Demo")
-                .claim("family_name", "User")
-                .claim("nonce", "abadnonce")
-                .claim("nickname", "Dee")
-                .claim("azp", clientId)
-                .claim("name","Demo T. User")
-                .claim("acr","urn:mace:incommon:iap:silver")
-                .claim("amr", List.of("pwd", "otp"))
-                .claim("auth_time", new Date())
-                .issueTime(new Date())
-                .expirationTime(Date.from(Instant.now().plusSeconds(120)))
+                .issuer(getClaimValue(overrideClaims, "iss", "test-issuer", String.class))
+                .audience(getClaimValue(overrideClaims, "aud", List.of("test-client"), List.class))
+                .subject(getClaimValue(overrideClaims, "sub", "jdoe", String.class))
+                .claim("preferred_username", getClaimValue(overrideClaims, "preferred_username", "d.tu", String.class))
+                .claim("given_name", getClaimValue(overrideClaims, "given_name", "Demo", String.class))
+                .claim("family_name", getClaimValue(overrideClaims, "family_name", "User", String.class))
+                .claim("nonce", getClaimValue(overrideClaims, "nonce", "abadnonce", String.class))
+                .claim("nickname", getClaimValue(overrideClaims, "nickname", "Dee", String.class))
+                .claim("azp", getClaimValue(overrideClaims, "azp", "test-client", String.class))
+                .claim("name",getClaimValue(overrideClaims, "name", "Demo T. User", String.class))
+                .claim("acr",getClaimValue(overrideClaims, "acr", "urn:mace:incommon:iap:silver", String.class))
+                .claim("amr",getClaimValue(overrideClaims, "amr", List.of("pwd", "otp"), List.class))
+                .claim("auth_time", getClaimValue(overrideClaims, "auth_time", new Date(), Date.class))
+                .issueTime(getClaimValue(overrideClaims, "iat", new Date(), Date.class))
+                .expirationTime(getClaimValue(overrideClaims, "auth_time", 
+                        Date.from(Instant.now().plusSeconds(120)), Date.class))
                 .build();
     }
     
     /**
      * Build a basic {@link JWTClaimsSet} from the supplied parameters to mock a UserInfo response.
      * 
-     * @param opIssuerId the OP's issuer ID     * 
-     * @param audience the list of audiences
-     * @param sub the subject
+     * @param overrideClaims claims to override
      * 
      * @return the JWT claims set.
      */
-    public static JWTClaimsSet createBasicUserInfoClaims(final String opIssuerId, final List<String> audience, 
-            final String sub) {
+    public static JWTClaimsSet createBasicUserInfoClaims(final Map<String, Object> overrideClaims) {
         
         return new JWTClaimsSet.Builder()
-                .issuer(opIssuerId)
-                .audience(audience)
-                .subject(sub)
-                .claim("preferred_username", "d.tu")
-                .claim("given_name", "Demo")
-                .claim("family_name", "User")
-                .claim("nonce", "abadnonce")
-                .claim("nickname", "Dee")
-                .claim("name","Demo T. User")
+                .issuer(getClaimValue(overrideClaims, "iss", "test-issuer", String.class))
+                .audience(getClaimValue(overrideClaims, "aud", List.of("test-client"), List.class))
+                .subject(getClaimValue(overrideClaims, "sub", "jdoe", String.class))
+                .claim("preferred_username", getClaimValue(overrideClaims, "preferred_username", "d.tu", String.class))
+                .claim("given_name", getClaimValue(overrideClaims, "given_name", "Demo", String.class))
+                .claim("family_name", getClaimValue(overrideClaims, "family_name", "User", String.class))
+                .claim("nickname", getClaimValue(overrideClaims, "nickname", "Dee", String.class))
+                .claim("name",getClaimValue(overrideClaims, "name", "Demo T. User", String.class))
                 .build();
     }
     
+    /** 
+     * Extract any override from the overrides map, or return the default value if there is none.
+     * 
+     * @param <T> the type to look for and return
+     * @param overrideClaims the overrides map
+     * @param claimName the claim name
+     * @param defaultValue the default value if no override is found
+     * @param type the type to return
+     * 
+     * @return the override or the default value.
+     */
+    private static <T> T getClaimValue(final Map<String, Object> overrideClaims, final String claimName, 
+            final Object defaultValue,  final Class<T> type) {
+        
+        if (overrideClaims.containsKey(claimName)) {
+            final Object override = overrideClaims.get(claimName);
+            if (override == null) {
+                return null;
+            }
+            if (type.isInstance(override)) {
+                //check empty string case, set null if so
+                if (override instanceof String && ((String)override).isEmpty()) {
+                    return null;
+                }
+                return type.cast(override);
+            }
+        }
+        return type.cast(defaultValue);
+    }
+    
     /**
      * Create a JWT from the given payload. The JWT can either be plain, or signed and encrypted. If encrypted, it must
      * be signed. 
@@ -189,35 +213,32 @@ public final class TestTokenHelper {
     
     /**
      * Create a JWT UserInfo response. Can be a JWS or JWE depending on the input values.
-     * 
-     * @param opIssuerId the issuer
-     * @param audience the audience
-     * @param sub the subject
+     * @param overrideClaims claims to override
      * @param sigAlg the signature algorithm
      * @param jweAlg the encryption algorithm
      * @param enc the content encryption algorithm
      * @param sigKey the signing key
      * @param encKey the encryption key
+     * 
      * @return
      * @throws JOSEException on error
      * @throws ParseException on error
      */
-    public static JWT createJWTUserInfoResponse(final String opIssuerId, final List<String> audience, 
-            final String sub, @Nullable final JWSAlgorithm sigAlg, 
-            @Nullable final JWEAlgorithm jweAlg, @Nullable final EncryptionMethod enc, 
-            @Nullable final Credential sigKey, @Nullable final Credential encKey) throws JOSEException, ParseException {
+    public static JWT createJWTUserInfoResponse(final Map<String, Object> overrideClaims,
+            @Nullable final JWSAlgorithm sigAlg, @Nullable final JWEAlgorithm jweAlg, 
+            @Nullable final EncryptionMethod enc, @Nullable final Credential sigKey, @Nullable final Credential encKey) 
+                    throws JOSEException, ParseException {
         
-        final var payload = createBasicUserInfoClaims(opIssuerId, audience, sub);
+        final var payload = createBasicUserInfoClaims(overrideClaims);
         return createJWT(payload, sigAlg, jweAlg, enc, sigKey, encKey);
         
     }
     
     /**
-     * Create an access token response JSON object and build a suitable id_token to include in the response.
+     * Create an access token response JSON object and build a suitable id_token to include in the response. Any claim
+     * contained in the overrides list overrides the default value built by the basic claims method.
      * 
-     * @param opIssuerId the issuer
-     * @param clientId the client_id
-     * @param audience the audience
+     * @param overrideClaims claims to override in the id_token
      * @param sigAlg the signature algorithm
      * @param jweAlg the encryption algorithm
      * @param enc the content encryption algorithm
@@ -228,33 +249,31 @@ public final class TestTokenHelper {
      * 
      * @throws Exception on error.
      */
-    public static String createAccessTokenResponseJSON(
-            final String opIssuerId, final String clientId, 
-            final List<String> audience, @Nullable final JWSAlgorithm sigAlg, 
+    public static String createAccessTokenResponseJSON(final Map<String, Object> overrideClaims,
+            @Nullable final JWSAlgorithm sigAlg, 
             @Nullable final JWEAlgorithm jweAlg, @Nullable final EncryptionMethod enc, 
             @Nullable final Credential sigKey, @Nullable final Credential encKey) throws Exception {
         
         
-        final var payload = createBasicClaims(opIssuerId, clientId, audience);
+        final var payload = createBasicClaims(overrideClaims);
         final JWT idToken = createJWT(payload, sigAlg, jweAlg, enc, sigKey, encKey);
         
         return buildTemplateAccessTokenJSONResponse(idToken.serialize());
     }
     
+    
     /**
      * Create a plain JSON object based UserInfo response.
      * 
-     * @param opIssuerId the issuer
-     * @param audience the audience
-     * @param sub the subject.
+     * @param overrideClaims claims to override
+     * 
      * @return a plain JSON object representing the UserInfo response
      * 
      * @throws JsonProcessingException on error
      */
-    public static String createPlainUserInfoResponseString(final String opIssuerId, final List<String> audience, 
-            final String sub) 
+    public static String createPlainUserInfoResponseString(final Map<String, Object> overrideClaims) 
             throws JsonProcessingException {
-        final JWTClaimsSet claims = createBasicUserInfoClaims(opIssuerId, audience, sub);
+        final JWTClaimsSet claims = createBasicUserInfoClaims(overrideClaims);
         return new ObjectMapper().writeValueAsString(claims.toJSONObject());
     }
     
@@ -262,15 +281,16 @@ public final class TestTokenHelper {
     /**
      * Create a Plain UserInfo response JWT.
      * 
-     * @param issuer the issuer
-     * @param audience the audience
+     * @param overrideClaims claims to override
+     * 
      * @return the signed JWT
+     * 
      * @throws JOSEException on error
      */
-    public static PlainJWT createPlainJWTUserInfoResponseJSON(final String issuer, final String audience) 
+    public static PlainJWT createPlainJWTUserInfoResponseJSON(final Map<String, Object> overrideClaims) 
             throws JOSEException {        
         
-        final var payload = createBasicUserInfoClaims(issuer, List.of(audience), "jdoe");        
+        final var payload = createBasicUserInfoClaims(overrideClaims);        
         return new PlainJWT(payload);
     }
 

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


More information about the commits mailing list