[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