[java-idp-oidc] branch dev/JOIDC-222 updated: JOIDC-222 - Support for OpenID Federation
Henri Mikkonen
henri.mikkonen at iki.fi
Wed Apr 23 09:44:44 UTC 2025
This is an automated email from the git hooks/post-receive script.
hjmikkon pushed a commit to branch dev/JOIDC-222
in repository java-idp-oidc.
View the commit online:
http://git.shibboleth.net/view/?p=java-idp-oidc.git;a=commit;h=f0324c3df50f3ff780481978253e7263485ad1a7
The following commit(s) were added to refs/heads/dev/JOIDC-222 by this push:
new f0324c3d JOIDC-222 - Support for OpenID Federation
f0324c3d is described below
commit f0324c3df50f3ff780481978253e7263485ad1a7
Author: Henri Mikkonen <henri.mikkonen at iki.fi>
AuthorDate: Wed Apr 23 12:43:07 2025 +0300
JOIDC-222 - Support for OpenID Federation
https://shibboleth.atlassian.net/browse/JOIDC-222
Switched explicit registration flow to use entity ID as client ID.
- Possibly existing records are replaced by the new metadata
- Refactored StoreClientInformation
---
.../op/oidfed/profile/impl/GenerateClientID.java | 140 +++++++++++++++++++++
.../op/profile/impl/StoreClientInformation.java | 63 +++-------
...faultClientInformationReplacementCondition.java | 58 +++++++++
.../idp/flows/oidc/register/register-beans.xml | 3 +
.../idp/flows/oidfed/register/register-beans.xml | 9 +-
.../profile/flow/oidfed/RegistrationFlowTest.java | 63 +++++-----
6 files changed, 255 insertions(+), 81 deletions(-)
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/GenerateClientID.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/GenerateClientID.java
new file mode 100644
index 00000000..6ee91f5c
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/GenerateClientID.java
@@ -0,0 +1,140 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.plugin.oidc.op.oidfed.profile.impl;
+
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
+import org.opensaml.profile.action.ActionSupport;
+import org.opensaml.profile.action.EventIds;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.profile.context.navigate.OutboundMessageContextLookup;
+import org.slf4j.Logger;
+
+import com.nimbusds.oauth2.sdk.id.ClientID;
+
+import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCClientRegistrationResponseContext;
+import net.shibboleth.idp.profile.AbstractProfileAction;
+import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
+import net.shibboleth.shared.component.ComponentInitializationException;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.primitive.LoggerFactory;
+
+/**
+ * Reuses the entity ID as the client ID for the registration.
+ */
+public class GenerateClientID extends AbstractProfileAction {
+
+ /** Class logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(GenerateClientID.class);
+
+ /** Strategy used to locate the {@link OIDCClientRegistrationResponseContext} associated with a given request. */
+ @Nonnull private Function<ProfileRequestContext,OIDCClientRegistrationResponseContext>
+ oidcResponseContextLookupStrategy;
+
+ /** Strategy used to obtain the client id value for authorize/token request. */
+ @NonnullAfterInit private Function<MessageContext, ClientID> clientIDLookupStrategy;
+
+ /** The OIDCClientRegistrationResponseContext to create the client ID to. */
+ @Nullable private OIDCClientRegistrationResponseContext oidcResponseCtx;
+
+ /** The entity id to be used as client id. */
+ @NonnullBeforeExec private String entityId;
+
+ /** Constructor. */
+ public GenerateClientID() {
+ final Function<ProfileRequestContext,OIDCClientRegistrationResponseContext> orcls =
+ new ChildContextLookup<>(OIDCClientRegistrationResponseContext.class).compose(
+ new OutboundMessageContextLookup());
+ assert orcls != null;
+ oidcResponseContextLookupStrategy = orcls;
+ }
+
+ /**
+ * Set the strategy used to locate the {@link OIDCClientRegistrationResponseContext} associated with a given
+ * {@link MessageContext}.
+ *
+ * @param strategy strategy used to locate the {@link OIDCClientRegistrationResponseContext} associated with a
+ * given {@link MessageContext}
+ */
+ public void setOidcResponseContextLookupStrategy(
+ @Nonnull final Function<ProfileRequestContext,OIDCClientRegistrationResponseContext> strategy) {
+ checkSetterPreconditions();
+
+ oidcResponseContextLookupStrategy = Constraint.isNotNull(strategy,
+ "OIDCClientRegistrationResponseContext lookup strategy cannot be null");
+ }
+
+ /**
+ * Set the strategy used to locate the client id of the request.
+ *
+ * @param strategy lookup strategy
+ */
+ public void setClientIDLookupStrategy(@Nonnull final Function<MessageContext, ClientID> strategy) {
+ checkSetterPreconditions();
+ clientIDLookupStrategy =
+ Constraint.isNotNull(strategy, "ClientIDLookupStrategy cannot be null");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void doInitialize() throws ComponentInitializationException {
+ super.doInitialize();
+
+ if (clientIDLookupStrategy == null) {
+ throw new ComponentInitializationException("ClientIDLookupStrategy cannot be null");
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
+
+ if (!super.doPreExecute(profileRequestContext)) {
+ return false;
+ }
+
+ oidcResponseCtx = oidcResponseContextLookupStrategy.apply(profileRequestContext);
+ if (oidcResponseCtx == null) {
+ log.debug("{} No OIDC client registration response context associated with this profile request",
+ getLogPrefix());
+ ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MSG_CTX);
+ return false;
+ }
+
+ final ClientID clientId = clientIDLookupStrategy.apply(profileRequestContext.getInboundMessageContext());
+ if (clientId == null || clientId.getValue() == null) {
+ log.error("{} No client ID could be resolved via inbound message context", getLogPrefix());
+ ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MSG_CTX);
+ return false;
+ }
+ entityId = clientId.getValue();
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
+ assert oidcResponseCtx != null;
+ oidcResponseCtx.setClientId(entityId);
+ }
+
+}
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/StoreClientInformation.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/StoreClientInformation.java
index b74e4457..52716dd6 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/StoreClientInformation.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/StoreClientInformation.java
@@ -17,6 +17,7 @@ package net.shibboleth.idp.plugin.oidc.op.profile.impl;
import java.time.Duration;
import java.time.Instant;
import java.util.function.Function;
+import java.util.function.Predicate;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -30,10 +31,8 @@ import com.nimbusds.oauth2.sdk.client.ClientInformation;
import com.nimbusds.openid.connect.sdk.rp.OIDCClientInformation;
import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCClientRegistrationResponseContext;
-import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCClientRegistrationTokenClaimsContext;
import net.shibboleth.idp.plugin.oidc.op.messaging.context.navigate.DefaultClientInformationLookupFunction;
-import net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.DefaultOIDCClientRegistrationTokenClaimsContextLookupFunction;
-import net.shibboleth.idp.plugin.oidc.op.token.support.RegistrationClaimsSet;
+import net.shibboleth.idp.plugin.oidc.op.profile.logic.DefaultClientInformationReplacementCondition;
import net.shibboleth.idp.profile.AbstractProfileAction;
import net.shibboleth.oidc.metadata.ClientInformationManager;
import net.shibboleth.oidc.metadata.ClientInformationManagerException;
@@ -57,20 +56,16 @@ public class StoreClientInformation extends AbstractProfileAction {
/** Strategy to obtain registration validity period policy. */
@Nullable private Function<ProfileRequestContext,Duration> registrationValidityPeriodStrategy;
- /** Strategy used to locate the {@link OIDCClientRegistrationTokenClaimsContext} associated with the request. */
- @Nonnull private Function<ProfileRequestContext,OIDCClientRegistrationTokenClaimsContext>
- registrationTokenContextLookupStrategy;
-
/** Strategy used to locate {@link OIDCClientInformation} to be stored. */
@Nonnull private Function<ProfileRequestContext,OIDCClientInformation> clientInformationLookupStrategy;
- /** The OIDCClientRegistrationTokenClaimsContext from which to optionally obtain client ID. */
- @Nullable private OIDCClientRegistrationTokenClaimsContext registrationTokenCtx;
-
+ /** Condition used to determine if existing record should be replaced. */
+ @Nonnull private Predicate<ProfileRequestContext> replacementCondition;
+
/** Constructor. */
public StoreClientInformation() {
- registrationTokenContextLookupStrategy = new DefaultOIDCClientRegistrationTokenClaimsContextLookupFunction();
clientInformationLookupStrategy = new DefaultClientInformationLookupFunction();
+ replacementCondition = new DefaultClientInformationReplacementCondition();
}
/**
@@ -102,30 +97,27 @@ public class StoreClientInformation extends AbstractProfileAction {
ifInitializedThrowUnmodifiabledComponentException();
clientInformationManager = Constraint.isNotNull(manager, "The client information manager cannot be null!");
}
-
+
/**
- * Set the strategy used to locate the {@link OIDCClientRegistrationTokenClaimsContext} associated with a given
- * request.
+ * Set the strategy used to locate {@link OIDCClientInformation} to be stored.
*
* @param strategy lookup strategy
*/
- public void setRegistrationTokenContextLookupStrategy(
- @Nonnull final Function<ProfileRequestContext,OIDCClientRegistrationTokenClaimsContext> strategy) {
- ifInitializedThrowUnmodifiabledComponentException();
+ public void setClientInformationLookupStrategy(@Nonnull final Function<ProfileRequestContext,OIDCClientInformation> strategy) {
+ checkSetterPreconditions();
- registrationTokenContextLookupStrategy = Constraint.isNotNull(strategy,
- "OIDCClientRegistrationTokenClaimsContext lookup strategy cannot be null");
+ clientInformationLookupStrategy = Constraint.isNotNull(strategy, "Client information lookup strategy cannot be null");
}
/**
- * Set the strategy used to locate {@link OIDCClientInformation} to be stored.
+ * Set the condition used to determine if existing record should be replaced.
*
- * @param strategy lookup strategy
+ * @param condition replacement condition
*/
- public void setClientInformationLookupStrategy(@Nonnull final Function<ProfileRequestContext,OIDCClientInformation> strategy) {
+ public void setReplacementCondition(@Nonnull final Predicate<ProfileRequestContext> condition) {
checkSetterPreconditions();
- clientInformationLookupStrategy = Constraint.isNotNull(strategy, "Client information lookup strategy cannot be null");
+ replacementCondition = Constraint.isNotNull(condition, "Replacement condition cannot be null");
}
/** {@inheritDoc} */
@@ -138,21 +130,6 @@ public class StoreClientInformation extends AbstractProfileAction {
}
}
- /** {@inheritDoc} */
- @Override
- protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
- if (!super.doPreExecute(profileRequestContext)) {
- return false;
- }
-
- registrationTokenCtx = registrationTokenContextLookupStrategy.apply(profileRequestContext);
- if (registrationTokenCtx != null && registrationTokenCtx.getClaimsSet() == null) {
- registrationTokenCtx = null;
- }
-
- return true;
- }
-
/** {@inheritDoc} */
@Override
protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
@@ -166,15 +143,7 @@ public class StoreClientInformation extends AbstractProfileAction {
Duration lifetime = registrationValidityPeriodStrategy != null ?
registrationValidityPeriodStrategy.apply(profileRequestContext) : null;
- final boolean replace;
- if (registrationTokenCtx != null) {
- final RegistrationClaimsSet claimsSet = registrationTokenCtx.getClaimsSet();
- assert claimsSet != null;
- replace = claimsSet.isReplacement();
-
- } else {
- replace = false;
- }
+ final boolean replace = replacementCondition.test(profileRequestContext);
log.debug("{} Storing client information (replace = {})", getLogPrefix(), replace);
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/logic/DefaultClientInformationReplacementCondition.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/logic/DefaultClientInformationReplacementCondition.java
new file mode 100644
index 00000000..8c65a9d8
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/logic/DefaultClientInformationReplacementCondition.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.plugin.oidc.op.profile.logic;
+
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCClientRegistrationTokenClaimsContext;
+import net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.DefaultOIDCClientRegistrationTokenClaimsContextLookupFunction;
+import net.shibboleth.idp.plugin.oidc.op.token.support.RegistrationClaimsSet;
+
+/**
+ * Default condition that bases on {@link RegistrationClaimsSet#isReplacement()} if it's set in
+ * {@link OIDCClientRegistrationTokenClaimsContext}. Otherwise, false is returned.
+ */
+public class DefaultClientInformationReplacementCondition implements Predicate<ProfileRequestContext> {
+
+ /** Strategy used to locate the {@link OIDCClientRegistrationTokenClaimsContext} associated with the request. */
+ @Nonnull private Function<ProfileRequestContext,OIDCClientRegistrationTokenClaimsContext>
+ registrationTokenContextLookupStrategy;
+
+ /**
+ * Constructor.
+ */
+ public DefaultClientInformationReplacementCondition() {
+ registrationTokenContextLookupStrategy = new DefaultOIDCClientRegistrationTokenClaimsContextLookupFunction();
+
+ }
+ /** {@inheritDoc} */
+ @Override
+ public boolean test(@Nullable final ProfileRequestContext profileRequestContext) {
+ final OIDCClientRegistrationTokenClaimsContext registrationTokenCtx =
+ registrationTokenContextLookupStrategy.apply(profileRequestContext);
+ if (registrationTokenCtx != null && registrationTokenCtx.getClaimsSet() != null) {
+ final RegistrationClaimsSet claimsSet = registrationTokenCtx.getClaimsSet();
+ assert claimsSet != null;
+ return claimsSet.isReplacement();
+ }
+ return false;
+ }
+}
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/register/register-beans.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/register/register-beans.xml
index 141cc452..11b19881 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/register/register-beans.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/register/register-beans.xml
@@ -109,6 +109,9 @@
<property name="registrationValidityPeriodStrategy">
<bean class="net.shibboleth.oidc.profile.config.navigate.RegistrationValidityPeriodLookupFunction" />
</property>
+ <property name="replacementCondition">
+ <bean class="net.shibboleth.idp.plugin.oidc.op.profile.logic.DefaultClientInformationReplacementCondition" />
+ </property>
</bean>
<bean id="BuildErrorResponseFromEvent"
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidfed/register/register-beans.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidfed/register/register-beans.xml
index 5a60fb0e..cc1e301a 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidfed/register/register-beans.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidfed/register/register-beans.xml
@@ -174,11 +174,9 @@
</bean>
<bean id="GenerateClientID"
- class="net.shibboleth.idp.plugin.oidc.op.profile.impl.GenerateClientID"
+ class="net.shibboleth.idp.plugin.oidc.op.oidfed.profile.impl.GenerateClientID"
scope="prototype"
- p:xmlSafeIdentifier="%{idp.oidc.xmlSafeIdentifiers:true}"
- p:relyingPartyContextLookupStrategy-ref="ExplicitRegistrationRelyingPartyCreationStrategy"
- p:identifierGeneratorLookupStrategy="#{getObject('shibboleth.oidc.dynreg.ClientIDGenerationStrategy') ?: getObject('shibboleth.oidc.DefaultIdentifierGenerationStrategy')}"/>
+ p:clientIDLookupStrategy-ref="shibboleth.ClientIDLookupStrategy" />
<bean id="GenerateClientSecret"
class="net.shibboleth.idp.plugin.oidc.op.profile.impl.GenerateClientSecret" scope="prototype"
@@ -218,7 +216,8 @@
<bean id="StoreClientInformation"
class="net.shibboleth.idp.plugin.oidc.op.profile.impl.StoreClientInformation" scope="prototype"
- p:clientInformationManager-ref="#{'%{idp.oidfed.expreg.clientInformationManager:shibboleth.oidc.ClientInformationManager}'.trim()}">
+ p:clientInformationManager-ref="#{'%{idp.oidfed.expreg.clientInformationManager:shibboleth.oidc.ClientInformationManager}'.trim()}"
+ p:replacementCondition-ref="shibboleth.Conditions.TRUE">
<property name="registrationValidityPeriodStrategy">
<bean parent="shibboleth.Functions.Expression"
c:expression="T(java.time.Duration).between(T(java.time.Instant).now(), #input.ensureInboundMessageContext().ensureSubcontext(T(net.shibboleth.idp.plugin.oidc.op.oidfed.profile.impl.RelyingPartyTrustChainContext)).getSelectedMetadataExpiration())" />
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/RegistrationFlowTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/RegistrationFlowTest.java
index 22cd0b55..e048dabf 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/RegistrationFlowTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/RegistrationFlowTest.java
@@ -14,6 +14,7 @@
package net.shibboleth.idp.plugin.oidc.op.profile.flow.oidfed;
+import java.io.IOException;
import java.util.List;
import org.opensaml.storage.StorageRecord;
@@ -24,6 +25,7 @@ import org.springframework.webflow.executor.FlowExecutionResult;
import org.testng.Assert;
import org.testng.annotations.Test;
+import com.nimbusds.oauth2.sdk.ParseException;
import com.nimbusds.openid.connect.sdk.federation.entities.EntityStatement;
import com.nimbusds.openid.connect.sdk.federation.entities.EntityStatementClaimsSet;
import com.nimbusds.openid.connect.sdk.rp.OIDCClientInformation;
@@ -78,31 +80,18 @@ public class RegistrationFlowTest extends AbstractFederationFlowTest {
configureMockHttpClient(clientId);
setRequest("POST", rpEntityConfiguration(clientId), "application/entity-statement+jwt");
final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
- final ExplicitClientRegistrationResponse parsedResponse =
- parseSuccessResponse(result, ExplicitClientRegistrationResponse.class);
- final EntityStatement entityStatement = parsedResponse.getEntityStatement();
- final EntityStatementClaimsSet statementClaims = entityStatement.getClaimsSet();
- Assert.assertEquals(statementClaims.getAuthorityHints().stream().map(id -> id.getValue()).toList(),
- List.of(anchorId));
- Assert.assertEquals(statementClaims.getClaim("trust_anchor"), anchorId);
- final OIDCClientInformation clientInfo = entityStatement.getClaimsSet().getRPInformation();
- final OIDCClientMetadata metadata = clientInfo.getOIDCMetadata();
- final String providedClientId = clientInfo.getID().getValue();
- assert providedClientId != null;
- assert storageService != null;
- final StorageRecord<String> storageRecord =
- storageService.read(BaseStorageServiceClientInformationComponent.CONTEXT_NAME, providedClientId);
- Assert.assertNotNull(storageRecord, "Record with clientId " + providedClientId + " was null");
- 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.assertTrue(metadata.getRedirectionURIStrings().contains(redirectUri));
+ assertResponseStatement(parseSuccessResponse(result, ExplicitClientRegistrationResponse.class), clientId);
+ }
+
+ @Test
+ public void testValidEntityConfiguration_repeat() throws Exception {
+ final String clientId = uniqueClientId();
+ for (int i = 0; i < 2; i++) {
+ configureMockHttpClient(clientId);
+ setRequest("POST", rpEntityConfiguration(clientId), "application/entity-statement+jwt");
+ final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+ assertResponseStatement(parseSuccessResponse(result, ExplicitClientRegistrationResponse.class), clientId);
+ }
}
@Test
@@ -112,9 +101,24 @@ public class RegistrationFlowTest extends AbstractFederationFlowTest {
subordinateStatement(clientId) + "\", \"" + trustedAnchorConfiguration() + "\"]";
setRequest("POST", trustChain, "application/trust-chain+json");
final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
- final ExplicitClientRegistrationResponse parsedResponse =
- parseSuccessResponse(result, ExplicitClientRegistrationResponse.class);
- final EntityStatement entityStatement = parsedResponse.getEntityStatement();
+ assertResponseStatement(parseSuccessResponse(result, ExplicitClientRegistrationResponse.class), clientId);
+ }
+
+ @Test
+ public void testValidTrustChain_repeat() throws Exception {
+ final String clientId = uniqueClientId();
+ final String trustChain = "[\"" + rpEntityConfiguration(clientId) + "\", \"" +
+ subordinateStatement(clientId) + "\", \"" + trustedAnchorConfiguration() + "\"]";
+ for (int i = 0; i < 2; i++) {
+ setRequest("POST", trustChain, "application/trust-chain+json");
+ final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+ assertResponseStatement(parseSuccessResponse(result, ExplicitClientRegistrationResponse.class), clientId);
+ }
+ }
+
+ protected void assertResponseStatement(final ExplicitClientRegistrationResponse response,
+ final String expectedClientId) throws IOException, ParseException, net.minidev.json.parser.ParseException {
+ final EntityStatement entityStatement = response.getEntityStatement();
final EntityStatementClaimsSet statementClaims = entityStatement.getClaimsSet();
Assert.assertEquals(statementClaims.getAuthorityHints().stream().map(id -> id.getValue()).toList(),
List.of(anchorId));
@@ -123,6 +127,7 @@ public class RegistrationFlowTest extends AbstractFederationFlowTest {
final OIDCClientMetadata metadata = clientInfo.getOIDCMetadata();
final String providedClientId = clientInfo.getID().getValue();
assert providedClientId != null;
+ Assert.assertEquals(providedClientId, expectedClientId);
assert storageService != null;
final StorageRecord<String> storageRecord =
storageService.read(BaseStorageServiceClientInformationComponent.CONTEXT_NAME, providedClientId);
@@ -139,5 +144,5 @@ public class RegistrationFlowTest extends AbstractFederationFlowTest {
Assert.assertTrue(metadata.getRedirectionURIStrings().contains(redirectUri));
}
-
+
}
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list