[java-idp-oidc] branch main updated: JOIDC-201 - Support for OAuth 2.0 Demonstrating Proof of Possession (DPoP)
Henri Mikkonen
henri.mikkonen at iki.fi
Thu May 23 07:51:27 UTC 2024
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=1c8d3b2fa1eaf3efe4f073790ea61b41d8f78eea
The following commit(s) were added to refs/heads/main by this push:
new 1c8d3b2f JOIDC-201 - Support for OAuth 2.0 Demonstrating Proof of Possession (DPoP)
1c8d3b2f is described below
commit 1c8d3b2fa1eaf3efe4f073790ea61b41d8f78eea
Author: Henri Mikkonen <henri.mikkonen at iki.fi>
AuthorDate: Thu May 23 10:51:10 2024 +0300
JOIDC-201 - Support for OAuth 2.0 Demonstrating Proof of Possession (DPoP)
https://shibboleth.atlassian.net/browse/JOIDC-201
Included support for dpop_bound_access_tokens flag in metadata
- supported in static and dynamic client registration
- the flag enforces the use of DPoP proofs in the token endpoint
---
.../impl/AddRemainingClaimsToClientMetadata.java | 6 +++--
.../dpop-proof-validation-beans.xml | 3 +++
.../idp/flows/oidc/token/token-beans.xml | 3 +++
.../op/profile/flow/PushedAuthorizeFlowTest.java | 11 ++++++++
.../oidc/op/profile/flow/RegistrationFlowTest.java | 28 +++++++++++++++++++++
.../plugin/oidc/op/profile/flow/TokenFlowTest.java | 29 ++++++++++++++++++++++
.../shibboleth/idp/module/conf/relying-party.xml | 1 +
7 files changed, 79 insertions(+), 2 deletions(-)
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/AddRemainingClaimsToClientMetadata.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/AddRemainingClaimsToClientMetadata.java
index 31d3270c..2ef21a9f 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/AddRemainingClaimsToClientMetadata.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/AddRemainingClaimsToClientMetadata.java
@@ -34,8 +34,9 @@ import net.shibboleth.shared.logic.Constraint;
import net.shibboleth.shared.primitive.LoggerFactory;
/**
- * Adds the remaining claims from the input metadata to the output {@link OIDCClientMetadata}, which haven't been
- * added there already, but which are recognized in the metadata policy.
+ * Adds the remaining recognized claims from the input metadata to the output {@link OIDCClientMetadata}. The static
+ * set of remaining recognized claims contain only 'dpop_bound_access_tokens'. The metadata policy offers a dynamic
+ * way to add any other claims to be recognized and thus included in the output metadata.
*/
public class AddRemainingClaimsToClientMetadata extends AbstractOIDCClientMetadataPopulationAction {
@@ -101,6 +102,7 @@ public class AddRemainingClaimsToClientMetadata extends AbstractOIDCClientMetada
/** {@inheritDoc} */
@Override
protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
+ getOutputMetadata().setDPoPBoundAccessTokens(getInputMetadata().getDPoPBoundAccessTokens());
final JSONObject input = getInputMetadata().toJSONObject();
final JSONObject initialOutput = getOutputMetadata().toJSONObject();
for (final String claim : input.keySet()) {
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oauth2/dpop-proof-validation/dpop-proof-validation-beans.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oauth2/dpop-proof-validation/dpop-proof-validation-beans.xml
index f2bcdf26..1dfb2bc7 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oauth2/dpop-proof-validation/dpop-proof-validation-beans.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oauth2/dpop-proof-validation/dpop-proof-validation-beans.xml
@@ -40,6 +40,9 @@
<bean class="net.shibboleth.oidc.profile.config.logic.RequireDPoPProofPredicate"/>
<bean class="net.shibboleth.idp.plugin.oidc.op.profile.logic.DPoPAccessTokenInRequestCondition"
p:httpServletRequestSupplier-ref="shibboleth.HttpServletRequestSupplier"/>
+ <bean parent="shibboleth.Conditions.Expression"
+ p:customObject="#{getObject('shibboleth.oidc.MetadataEnforcedDPoP')}"
+ c:expression="#custom != null and #custom.test(#input)" />
</list>
</constructor-arg>
</bean>
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 157d0308..f62f4b4b 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
@@ -15,6 +15,9 @@
<bean id="shibboleth.oidc.loggingId" class="java.lang.String" c:_0="%{idp.service.logging.oidctoken:OIDC.Token}" />
+ <bean id="shibboleth.oidc.MetadataEnforcedDPoP" parent="shibboleth.Conditions.Expression"
+ c:expression="#input.ensureInboundMessageContext().getSubcontext(T(net.shibboleth.oidc.metadata.context.OIDCMetadataContext)) != null and #input.ensureInboundMessageContext().getSubcontext(T(net.shibboleth.oidc.metadata.context.OIDCMetadataContext)).getClientInformation() != null and #input.ensureInboundMessageContext().getSubcontext(T(net.shibboleth.oidc.metadata.context.OIDCMetadataContext)).getClientInformation().getMetadata().getDPoPBoundAccessTokens()" />
+
<util:constant id="shibboleth.metrics.ProfileCounter"
static-field="net.shibboleth.oidc.profile.oauth2.config.impl.DefaultOAuth2TokenConfiguration.PROFILE_COUNTER" />
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/PushedAuthorizeFlowTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/PushedAuthorizeFlowTest.java
index e4ae2607..d5836eba 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/PushedAuthorizeFlowTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/PushedAuthorizeFlowTest.java
@@ -77,6 +77,7 @@ public class PushedAuthorizeFlowTest extends AbstractOidcClientAuthenticationFlo
removeMetadata(storageService, clientId);
removeMetadata(storageService, clientIdNotMDDriven);
removeMetadata(storageService, "mockPublicClientIdPKCEPlainUnforced");
+ removeMetadata(storageService, "mockClientIdDPoPAccessToken");
}
@Test
@@ -193,6 +194,16 @@ public class PushedAuthorizeFlowTest extends AbstractOidcClientAuthenticationFlo
removeMetadata(storageService, "mockClientIdRequestObjectEnforced");
}
+ @Test
+ public void testWithMissingRequiredDPoPProof() throws IOException, SessionException {
+ final String clientId = "mockClientIdDPoPAccessToken";
+ storeMetadata(storageService, clientId, clientSecret, scope, "https://example.org/cb");
+ setBasicAuth(clientId, clientSecret);
+ setHttpFormRequest("POST", createRequestParameters(clientId));
+ final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+ assertErrorCode(result, OAuth2Error.INVALID_DPOP_PROOF_CODE);
+ }
+
@Test
public void testWithInvalidSyntaxDPoPProof() throws IOException, SessionException {
storeMetadata(storageService, clientId, clientSecret, scope, "https://example.org/cb");
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/RegistrationFlowTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/RegistrationFlowTest.java
index 27243f7c..b1aeacd4 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/RegistrationFlowTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/RegistrationFlowTest.java
@@ -172,6 +172,34 @@ public class RegistrationFlowTest extends AbstractOidcFlowTest {
Assert.assertTrue(storedInfo.getOIDCMetadata().requiresBackChannelLogoutSession());
}
+ @Test
+ public void testUnauthenticated_successWithDpop() throws Exception {
+ final String requestUri = "https://client.example.org/rf.txt#qpXaRLh_n93TTR9F252ValdatUQvQiJi5BDub2BeznA";
+ setJsonRequest("POST", "{ \"redirect_uris\":[\"" + redirectUri + "\"], \"request_uris\":[\"" + requestUri + "\"],"
+ + "\"dpop_bound_access_tokens\":true }");
+ final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+ final OIDCClientInformationResponse parsedResponse =
+ parseSuccessResponse(result, OIDCClientInformationResponse.class);
+ final OIDCClientInformation clientInfo = parsedResponse.getOIDCClientInformation();
+ final OIDCClientMetadata metadata = clientInfo.getOIDCMetadata();
+ final String clientId = clientInfo.getID().getValue();
+ assert clientId != null;
+ assert storageService != null;
+ final StorageRecord<String> storageRecord = storageService.read(BaseStorageServiceClientInformationComponent.CONTEXT_NAME,
+ clientId);
+ assert storageRecord != null;
+ final String record = storageRecord.getValue();
+ Assert.assertNotNull(record);
+ final JSONParser parser = new JSONParser(JSONParser.DEFAULT_PERMISSIVE_MODE);
+ final OIDCClientInformation storedInfo = OIDCClientInformation.parse((JSONObject) parser.parse(record));
+ Assert.assertEquals(storedInfo.getID(), clientInfo.getID());
+ Assert.assertEquals(storedInfo.getSecret(), clientInfo.getSecret());
+ Assert.assertEquals(storedInfo.getOIDCMetadata().getRedirectionURIStrings(), metadata.getRedirectionURIStrings());
+ Assert.assertEquals(storedInfo.getOIDCMetadata().getRequestObjectURIs(), Set.of(new URI(requestUri)));
+ Assert.assertTrue(metadata.getRedirectionURIStrings().contains(redirectUri));
+ Assert.assertTrue(metadata.getDPoPBoundAccessTokens());
+ }
+
@Test
public void testAccessToken_nonCompliantWithProfilePolicy1() throws Exception {
setJsonRequest("POST", "{ \"redirect_uris\":[\"" + redirectUri + "\"] }");
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 30e790d7..d16c4760 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
@@ -25,6 +25,7 @@ import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import javax.annotation.Nonnull;
@@ -798,6 +799,34 @@ public class TokenFlowTest extends AbstractOidcClientAuthenticationFlowTest {
assertErrorCode(result, OAuth2Error.INVALID_DPOP_PROOF_CODE);
}
+ @Test
+ public void testDPoPMetadataEnforcedValidGrantNoProof() throws Exception {
+ final AuthorizeCodeClaimsSet.Builder builder = new AuthorizeCodeClaimsSet.Builder();
+ builder.setJWTID(new SecureRandomIdentifierGenerationStrategy())
+ .setClientID(new ClientID(clientId))
+ .setIssuer("https://op.example.org")
+ .setPrincipal("jdoe")
+ .setSubject("mock")
+ .setIssuedAt(Instant.now())
+ .setExpiresAt(Instant.now().plusSeconds(100))
+ .setAuthenticationTime(Instant.now())
+ .setRedirectURI(new URI(redirectUri))
+ .setScope(scope);
+ final String authorizationCode = builder.build().serialize(getDataSealer());
+ setHttpFormRequest("POST", createRequestParameters(redirectUri,
+ "authorization_code",
+ authorizationCode, clientId));
+ setBasicAuth(clientId, clientSecret);
+ final OIDCClientMetadata metadata = buildMetadataSkeleton();
+ metadata.setRedirectionURIs(Set.of(new URI(redirectUri)));
+ metadata.setScope(scope);
+ metadata.setDPoPBoundAccessTokens(true);
+ storeMetadataObject(storageService, clientId, clientSecret, metadata);
+ storeConsent(storageService, "jdoe", clientId, "mail");
+ final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+ assertErrorCode(result, OAuth2Error.INVALID_DPOP_PROOF_CODE);
+ }
+
@Test
public void testDPoPEnforcedValidGrantThumbprintIncludedNotMatchingProof() throws Exception {
final String clientId = clientIdDPoPAccessToken;
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 eb932bfa..c861e172 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
@@ -233,6 +233,7 @@
<property name="profileConfigurations">
<list>
<bean parent="OIDC.SSO.MDDriven" p:requireDpopProof="true"/>
+ <bean parent="OAUTH2.PAR" p:requireDpopProof="true"/>
</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