[java-idp-oidc] branch dev/JOIDC-7 updated: WIP - code grant working w/ JWT and extra audience

Scott Cantor cantor.2 at osu.edu
Mon Apr 25 17:14:58 UTC 2022


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

scantor pushed a commit to branch dev/JOIDC-7
in repository java-idp-oidc.

View the commit online:
http://git.shibboleth.net/view/?p=java-idp-oidc.git;a=commit;h=896dbf41811ae5b2c3b3d05a795e69698afd74b3

The following commit(s) were added to refs/heads/dev/JOIDC-7 by this push:
     new 896dbf41 WIP - code grant working w/ JWT and extra audience
896dbf41 is described below

commit 896dbf41811ae5b2c3b3d05a795e69698afd74b3
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Mon Apr 25 13:14:55 2022 -0400

    WIP - code grant working w/ JWT and extra audience
---
 .../navigate/ClientInfoAudienceLookupFunction.java |   3 +
 .../op/oauth2/profile/impl/ValidateAudience.java   |  25 +++--
 .../idp/flows/oidc/token/token-beans.xml           |  10 +-
 .../shibboleth/idp/flows/oidc/token/token-flow.xml |   2 +-
 .../idp/flows/oidc/userinfo/userinfo-beans.xml     |  10 +-
 .../idp/flows/oidc/userinfo/userinfo-flow.xml      |   1 +
 .../idp/service/relying-party/postconfig.xml       |   3 +-
 .../profile/impl/ValidateAccessTokenTest.java      | 103 +++++++++------------
 ...onfig.xml.off => oauth2client-authn-config.xml} |   0
 9 files changed, 74 insertions(+), 83 deletions(-)

diff --git a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/ClientInfoAudienceLookupFunction.java b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/ClientInfoAudienceLookupFunction.java
index 9ae72310..e4170897 100644
--- a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/ClientInfoAudienceLookupFunction.java
+++ b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/ClientInfoAudienceLookupFunction.java
@@ -20,6 +20,7 @@ package net.shibboleth.idp.plugin.oidc.op.profile.context.navigate;
 import java.net.URI;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
 
 import javax.annotation.Nonnull;
@@ -70,6 +71,8 @@ public class ClientInfoAudienceLookupFunction implements ContextDataLookupFuncti
             }
             
             return audience;
+        } else if (obj instanceof String) {
+            return Collections.singletonList((String) obj);
         }
         
         return null;
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/ValidateAudience.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/ValidateAudience.java
index 9818fccd..20ae0969 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/ValidateAudience.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/ValidateAudience.java
@@ -173,15 +173,6 @@ public class ValidateAudience extends AbstractOIDCAuthenticationResponseAction {
         
         // These may come from metadata or be supplemented or substituted from elsewhere.
         final List<String> allowedAudience = allowedAudienceLookupStrategy.apply(profileRequestContext);
-        if (allowedAudience == null || allowedAudience.isEmpty()) {
-            if (allowNone) {
-                log.debug("{} No allowed audience for client {}, OP will be sole audience", getLogPrefix(), clientId);
-            } else {
-                log.warn("{} No allowed audience for client {}", getLogPrefix(), clientId);
-                ActionSupport.buildEvent(profileRequestContext, OidcEventIds.INVALID_TARGET);
-            }
-            return;
-        }
         
         // These come from a previous authorization grant (authz code or refresh token).
         List<String> previouslyGrantedAudience = null;
@@ -192,7 +183,21 @@ public class ValidateAudience extends AbstractOIDCAuthenticationResponseAction {
         // These come from a request object or parameter. Absent by definition on the UserInfo endpoint.
         List<String> requestedAudience = requestedAudienceLookupStrategy != null ?
                 requestedAudienceLookupStrategy.apply(profileRequestContext) : null;
-        
+
+        if (allowedAudience == null || allowedAudience.isEmpty()) {
+            if (allowNone) {
+                if (previouslyGrantedAudience != null || requestedAudience != null) {
+                    log.warn("{} No allowed audiences for client {}, OP will be sole audience", getLogPrefix(), clientId);
+                } else {
+                    log.debug("{} No allowed audiences for client {}, OP will be sole audience", getLogPrefix(), clientId);
+                }
+            } else {
+                log.warn("{} No allowed audience for client {}", getLogPrefix(), clientId);
+                ActionSupport.buildEvent(profileRequestContext, OidcEventIds.INVALID_TARGET);
+            }
+            return;
+        }
+
         if (requestedAudience == null) {
             // With none requested, simply swap requested for previously granted, if any.
             // Set previous set to null since there's no need to filter against it.
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/token/token-beans.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/token/token-beans.xml
index bc6ce334..4d4f0962 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/token/token-beans.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/token/token-beans.xml
@@ -332,7 +332,6 @@
 
     <bean id="SignOIDCAccessToken"
             class="net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl.SignAccessToken" scope="prototype"
-            p:securityParametersLookupStrategy-ref="AudienceSecurityParametersCreationStrategy"
             p:typeHeader="at+jwt">
         <property name="securityParametersLookupStrategy">
             <bean parent="shibboleth.Functions.Compose"
@@ -373,11 +372,6 @@
                 c:g-ref="shibboleth.ChildLookupOrCreate.SecurityParameters"
                 c:f-ref="shibboleth.ChildLookup.RelyingParty" />
         </property>
-        <property name="existingParametersContextLookupStrategy">
-            <bean parent="shibboleth.Functions.Compose"
-                c:g-ref="shibboleth.ChildLookup.SecurityParameters"
-                c:f-ref="shibboleth.ChildLookup.RelyingParty" />
-        </property>
     </bean>
 
     <bean id="PopulateIDTokenEncryptionParameters"
@@ -449,7 +443,7 @@
 
     <!-- Third-party token actions. -->
 
-    <bean id="PopulateAccessTokenSignatureSigningParameters"
+    <bean id="PopulateThirdPartyAccessTokenSignatureSigningParameters"
         class="net.shibboleth.idp.plugin.oidc.op.profile.impl.PopulateOIDCSignatureSigningParameters"
         scope="prototype"
         c:strategy-ref="shibboleth.MessageContextLookup.Outbound"
@@ -461,7 +455,7 @@
         c:g-ref="shibboleth.ChildLookupOrCreate.SecurityParameters"
         c:f-ref="AudienceRelyingPartyCreationStrategy" />
         
-    <bean id="PopulateAccessTokenEncryptionParameters"
+    <bean id="PopulateThirdPartyAccessTokenEncryptionParameters"
             class="net.shibboleth.idp.plugin.oidc.op.profile.impl.PopulateOIDCEncryptionParameters" scope="prototype"
             p:encryptionOptionalPredicate-ref="AudienceEncryptionOptionalPredicate"
             p:oidcMetadataContextLookupStrategy-ref="LookupOutboundOIDCMetadataContext"
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/token/token-flow.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/token/token-flow.xml
index eed418f9..f1bba542 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/token/token-flow.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/token/token-flow.xml
@@ -171,7 +171,7 @@
     wouldn't be usable. If it were encrypted to our key then there would be no point to
     allowing it to be a JWT.
     
-    Not also no attribute claims are added to the access token since that isn't a proper
+    Note also no attribute claims are added to the access token since that isn't a proper
     delivery mechanism for claims to the OIDC client.
     -->
     <action-state id="BuildTokensForUserInfoAccess">
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/userinfo/userinfo-beans.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/userinfo/userinfo-beans.xml
index 04cce6cb..737439a3 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/userinfo/userinfo-beans.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/userinfo/userinfo-beans.xml
@@ -33,11 +33,13 @@
     <bean id="ParseAccessToken"
         class="net.shibboleth.idp.plugin.oidc.op.userinfo.profile.impl.ParseAccessToken" scope="prototype"
         p:dataSealer-ref="#{'%{idp.oidc.tokenSealer:shibboleth.oidc.TokenSealer}'.trim()}"
-        p:revocationCache-ref="shibboleth.oidc.RevocationCache" />
+        p:credentialResolver-ref="SigningCredentialsResolver" />
+
+    <bean id="SigningCredentialsResolver" class="net.shibboleth.idp.relyingparty.impl.SigningCredentialsResolver" 
+        c:_0-ref="shibboleth.RelyingPartyResolverService" />
 
     <bean id="ValidateAccessToken"
-        class="net.shibboleth.idp.plugin.oidc.op.userinfo.profile.impl.ValidateAccessToken" scope="prototype"
-        p:dataSealer-ref="#{'%{idp.oidc.tokenSealer:shibboleth.oidc.TokenSealer}'.trim()}" />
+        class="net.shibboleth.idp.plugin.oidc.op.userinfo.profile.impl.ValidateAccessToken" scope="prototype" />
         
     <bean id="ValidateScope" class="net.shibboleth.idp.plugin.oidc.op.profile.impl.ValidateScope" scope="prototype"
             p:allowedScopeLookupStrategy="#{getObject('shibboleth.oidc.AllowedScopeStrategy') ?: getObject('shibboleth.oidc.DefaultAllowedScopeStrategy')}">
@@ -120,7 +122,7 @@
         class="net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.UserInfoResponseClaimsSetLookupFunction" />
 
     <bean id="SignUserInfoResponse" class="net.shibboleth.idp.plugin.oidc.op.profile.impl.SignUserInfoResponse"
-        scope="prototype">
+            scope="prototype">
         <property name="securityParametersLookupStrategy">
             <bean parent="shibboleth.Functions.Compose"
                 c:g-ref="shibboleth.ChildLookup.SecurityParameters"
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/userinfo/userinfo-flow.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/userinfo/userinfo-flow.xml
index b0f4318c..be5f41c5 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/userinfo/userinfo-flow.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/userinfo/userinfo-flow.xml
@@ -16,6 +16,7 @@
     <action-state id="DecodeMessage">
         <evaluate expression="DecodeMessage" />
         <evaluate expression="PostDecodePopulateAuditContext" />
+        <evaluate expression="ParseAccessToken" />
         <evaluate expression="'proceed'" />
         
         <!-- DoMetadataLookup is expected to proceed to SelectConfiguration -->
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 ee263edd..455e4139 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
@@ -591,7 +591,8 @@
     </util:list>
 
     <bean id="OPInAudienceClaimsValidator"
-            class="net.shibboleth.oidc.security.jwt.claims.impl.AudienceClaimsValidator">
+            class="net.shibboleth.oidc.security.jwt.claims.impl.AudienceClaimsValidator"
+            p:allowMissing="true">
         <property name="audienceLookupStrategy">
             <bean class="net.shibboleth.utilities.java.support.logic.BiFunctionSupport"
                 factory-method="forFunctionOfFirstArg"
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/userinfo/profile/impl/ValidateAccessTokenTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/userinfo/profile/impl/ValidateAccessTokenTest.java
index a2f609e9..c613e05f 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/userinfo/profile/impl/ValidateAccessTokenTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/userinfo/profile/impl/ValidateAccessTokenTest.java
@@ -19,25 +19,32 @@ package net.shibboleth.idp.plugin.oidc.op.userinfo.profile.impl;
 
 import net.shibboleth.idp.plugin.oidc.op.profile.impl.BaseOIDCResponseActionTest;
 import net.shibboleth.idp.plugin.oidc.op.token.support.AccessTokenClaimsSet;
-import net.shibboleth.idp.plugin.oidc.op.token.support.AuthorizeCodeClaimsSet;
 import net.shibboleth.idp.plugin.oidc.op.token.support.TokenClaimsSet;
 import net.shibboleth.idp.profile.testing.ActionTestingSupport;
+import net.shibboleth.oidc.jwt.claims.ClaimsValidator;
 import net.shibboleth.oidc.profile.core.OidcEventIds;
+import net.shibboleth.oidc.security.jwt.claims.impl.AudienceClaimsValidator;
+import net.shibboleth.oidc.security.jwt.claims.impl.ChainingJWTClaimsValidator;
+import net.shibboleth.oidc.security.jwt.claims.impl.ExpiryClaimsValidator;
+import net.shibboleth.oidc.security.jwt.claims.impl.NotBeforeClaimsValidator;
+import net.shibboleth.oidc.security.jwt.claims.impl.RequiredClaimsValidator;
 import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.logic.BiFunctionSupport;
 import net.shibboleth.utilities.java.support.security.DataSealerException;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.security.NoSuchAlgorithmException;
 import java.time.Instant;
+import java.util.ArrayList;
 import java.util.Collections;
+import java.util.function.Function;
 
+import org.opensaml.profile.context.ProfileRequestContext;
 import org.springframework.webflow.execution.Event;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 import com.nimbusds.oauth2.sdk.Scope;
 import com.nimbusds.oauth2.sdk.id.ClientID;
-import com.nimbusds.oauth2.sdk.token.BearerAccessToken;
-import com.nimbusds.openid.connect.sdk.UserInfoRequest;
 
 // Checkstyle: ThrowsCount OFF
 
@@ -50,22 +57,10 @@ public class ValidateAccessTokenTest extends BaseOIDCResponseActionTest {
     @BeforeMethod
     private void init() throws ComponentInitializationException, NoSuchAlgorithmException {
         action = new ValidateAccessToken();
+        action.setClaimsValidatorLookupStrategy(new ClaimsValidatorLookup());
         action.initialize();
     }
 
-    /**
-     * Test that action throws error if revocation cache is not set.
-     * 
-     * @throws NoSuchAlgorithmException 
-     * @throws ComponentInitializationException 
-     */
-    @Test(expectedExceptions = ComponentInitializationException.class)
-    public void testNoRevocationCache() throws NoSuchAlgorithmException, ComponentInitializationException {
-        action = new ValidateAccessToken();
-        action.initialize();
-        action.execute(requestCtx);
-    }
-
     /**
      * Basic success case.
      * 
@@ -84,14 +79,13 @@ public class ValidateAccessTokenTest extends BaseOIDCResponseActionTest {
                 .setPrincipal("userPrin")
                 .setSubject("subject")
                 .setIssuedAt(Instant.now())
-                .setExpiresAt(Instant.now().plusSeconds(1))
+                .setExpiresAt(Instant.now().plusSeconds(300))
                 .setAuthenticationTime(Instant.now())
                 .setRedirectURI(new URI("http://example.com"))
                 .setScope(new Scope())
                 .build();
-        final BearerAccessToken token = new BearerAccessToken(claims.serialize(getDataSealer()));
-        final UserInfoRequest req = new UserInfoRequest(new URI("http://example.com"), token);
-        setUserInfoRequest(req);
+        respCtx.setAuthorizationGrantClaimsSet(claims);
+        
         final Event event = action.execute(requestCtx);
         ActionTestingSupport.assertProceedEvent(event);
     }
@@ -114,15 +108,14 @@ public class ValidateAccessTokenTest extends BaseOIDCResponseActionTest {
                 .setPrincipal("userPrin")
                 .setSubject("subject")
                 .setIssuedAt(Instant.now())
-                .setExpiresAt(Instant.now().plusSeconds(1))
+                .setExpiresAt(Instant.now().plusSeconds(300))
                 .setAuthenticationTime(Instant.now())
                 .setRedirectURI(new URI("http://example.com"))
                 .setScope(new Scope())
                 .setAudience(Collections.singletonList("foo"))
                 .build();
-        final BearerAccessToken token = new BearerAccessToken(claims.serialize(getDataSealer()));
-        final UserInfoRequest req = new UserInfoRequest(new URI("http://example.com"), token);
-        setUserInfoRequest(req);
+        respCtx.setAuthorizationGrantClaimsSet(claims);
+        
         final Event event = action.execute(requestCtx);
         ActionTestingSupport.assertEvent(event, OidcEventIds.INVALID_GRANT);
     }
@@ -145,48 +138,40 @@ public class ValidateAccessTokenTest extends BaseOIDCResponseActionTest {
                 .setPrincipal("userPrin")
                 .setSubject("subject")
                 .setIssuedAt(Instant.now())
-                .setExpiresAt(Instant.now().minusMillis(1))
+                .setExpiresAt(Instant.now().minusSeconds(120))
                 .setAuthenticationTime(Instant.now())
                 .setRedirectURI(new URI("http://example.com"))
                 .setScope(new Scope())
                 .build();
-        final BearerAccessToken token = new BearerAccessToken(claims.serialize(getDataSealer()));
-        final UserInfoRequest req = new UserInfoRequest(new URI("http://example.com"), token);
-        setUserInfoRequest(req);
+        respCtx.setAuthorizationGrantClaimsSet(claims);
+        
         final Event event = action.execute(requestCtx);
         ActionTestingSupport.assertEvent(event, OidcEventIds.INVALID_GRANT);
     }
 
-    /**
-     * Fails due token authz code is revoked. Test not 100% as it really does not test passing id to revocation cache.
-     * 
-     * @throws NoSuchAlgorithmException 
-     * @throws ComponentInitializationException 
-     * @throws URISyntaxException 
-     * @throws DataSealerException 
-     */
-    @Test
-    public void testFailsRevoked()
-            throws NoSuchAlgorithmException, ComponentInitializationException, URISyntaxException, DataSealerException {
-        action = new ValidateAccessToken();
-        action.initialize();
-        final TokenClaimsSet claims = new AccessTokenClaimsSet.Builder()
-                .setJWTID(idGenerator)
-                .setClientID(new ClientID())
-                .setIssuer("issuer")
-                .setPrincipal("userPrin")
-                .setSubject("subject")
-                .setIssuedAt(Instant.now())
-                .setExpiresAt(Instant.now().plusSeconds(1))
-                .setAuthenticationTime(Instant.now())
-                .setRedirectURI(new URI("http://example.com"))
-                .setScope(new Scope())
-                .build();
-        final BearerAccessToken token = new BearerAccessToken(claims.serialize(getDataSealer()));
-        final UserInfoRequest req = new UserInfoRequest(new URI("http://example.com"), token);
-        setUserInfoRequest(req);
-        final Event event = action.execute(requestCtx);
-        ActionTestingSupport.assertEvent(event, OidcEventIds.INVALID_GRANT);
-    }
+    private class ClaimsValidatorLookup implements Function<ProfileRequestContext,ClaimsValidator> {
 
+        public ClaimsValidator apply(ProfileRequestContext t) {
+            final ChainingJWTClaimsValidator chain = new ChainingJWTClaimsValidator();
+            chain.setId("test");
+            chain.setRequireAll(true);
+            
+            final ArrayList<ClaimsValidator> validators = new ArrayList<>();
+            final RequiredClaimsValidator req = new RequiredClaimsValidator();
+            req.setRequiredClaims(Collections.singletonList("jti"));
+            validators.add(req);
+            validators.add(new NotBeforeClaimsValidator());
+            validators.add(new ExpiryClaimsValidator());
+            final AudienceClaimsValidator aud = new AudienceClaimsValidator();
+            aud.setAudienceLookupStrategy(BiFunctionSupport.constant("issuer"));
+            aud.setAllowMissing(true);
+            validators.add(aud);
+            
+            chain.setClaimValidators(validators);
+            
+            return chain;
+        }
+        
+    }
+    
 }
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/test/resources/conf/authn/oauth2client-authn-config.xml.off b/idp-oidc-extension-impl/src/test/resources/conf/authn/oauth2client-authn-config.xml
similarity index 100%
rename from idp-oidc-extension-impl/src/test/resources/conf/authn/oauth2client-authn-config.xml.off
rename to idp-oidc-extension-impl/src/test/resources/conf/authn/oauth2client-authn-config.xml

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


More information about the commits mailing list