[java-idp-oidc] branch dev/JOIDC-222 updated: JOIDC-222 - Support for OpenID Federation
Henri Mikkonen
henri.mikkonen at iki.fi
Mon Mar 17 15:36:46 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=457614caf9b8a96eb67c0894f94e6c1d96788de8
The following commit(s) were added to refs/heads/dev/JOIDC-222 by this push:
new 457614ca JOIDC-222 - Support for OpenID Federation
457614ca is described below
commit 457614caf9b8a96eb67c0894f94e6c1d96788de8
Author: Henri Mikkonen <henri.mikkonen at iki.fi>
AuthorDate: Mon Mar 17 17:36:11 2025 +0200
JOIDC-222 - Support for OpenID Federation
https://shibboleth.atlassian.net/browse/JOIDC-222
Initial implementation for the explicit registration flow (oidfed/register)
- new abstract-register flow serves both OIDC and OIDFed register flows
- some of the actions of oidc/register were refactored to be able to serve both flows
- new profile configuration: OIDFederationExplicitRegistrationConfiguration
- Refactored automatic registration configuration to share the same abstract configuration
---
.../OIDCClientRegistrationResponseContext.java | 70 ++++--
.../DefaultClientInformationLookupFunction.java | 47 ++++
.../DefaultRequestedMetadataLookupFunction.java | 48 ++++
...ederationRegistrationProfileConfiguration.java} | 42 +---
...derationAutomaticRegistrationConfiguration.java | 131 +---------
...ederationExplicitRegistrationConfiguration.java | 195 +++++++++++++++
...derationAutomaticRegistrationConfiguration.java | 59 +----
...ederationExplicitRegistrationConfiguration.java | 50 ++++
...ederationRegistrationProfileConfiguration.java} | 28 +--
.../TokenEndpointAuthMethodLookupFunction.java | 55 +++++
.../ExplicitClientRegistrationRequestDecoder.java | 154 ++++++++++++
.../impl/ExplicitClientRegistrationRequest.java | 100 ++++++++
.../impl/ExplicitClientRegistrationResponse.java | 97 ++++++++
...ava => AbstractBuildEntityStatementAction.java} | 187 ++++++---------
.../profile/impl/BuildEntityConfiguration.java | 148 ++++++++++++
.../impl/BuildExplicitRegistrationResponse.java | 172 ++++++++++++++
.../impl/FormExplicitRegistrationResponse.java | 118 +++++++++
...ltSelectedTrustChainMetadataLookupStrategy.java | 76 ++++++
...tRegistrationRequestClientIDLookupFunction.java | 68 ++++++
...entRegistrationRequestJWKSetLookupFunction.java | 69 ++++++
.../LocalMetadataPolicyLookupFunction.java | 8 +-
.../MandatoryTrustMarksLookupFunction.java | 8 +-
.../MaximumTrustMarkLifetimeLookupFunction.java | 8 +-
.../impl/AbstractOIDCClientRegistrationAction.java | 98 ++++++++
.../op/profile/impl/BuildClientInformation.java | 71 +-----
.../oidc/op/profile/impl/CheckRedirectURIs.java | 44 ++--
...rmOutboundClientInformationResponseMessage.java | 50 ++++
.../op/profile/impl/StoreClientInformation.java | 39 ++-
.../oidc-abstract-register-beans.xml | 131 ++++++++++
.../oidc-abstract-register-flow.xml} | 35 +--
.../idp/flows/oidc/abstract/oidc-abstract-flow.xml | 5 +-
.../idp/flows/oidc/register/register-beans.xml | 98 +-------
.../idp/flows/oidc/register/register-flow.xml | 53 +----
.../entity-configuration-beans.xml | 2 +-
.../idp/flows/oidfed/register/register-beans.xml | 264 +++++++++++++++++++++
.../idp/flows/oidfed/register/register-flow.xml | 49 ++++
.../idp/service/relying-party/postconfig.xml | 5 +
.../profile/flow/oidfed/RegistrationFlowTest.java | 101 ++++++++
.../profile/impl/BuildClientInformationTest.java | 19 +-
.../op/profile/impl/CheckRedirectUrisTest.java | 2 +-
.../shibboleth/idp/module/conf/relying-party.xml | 1 +
41 files changed, 2319 insertions(+), 686 deletions(-)
diff --git a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/messaging/context/OIDCClientRegistrationResponseContext.java b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/messaging/context/OIDCClientRegistrationResponseContext.java
index 5ab7f7de..db612454 100644
--- a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/messaging/context/OIDCClientRegistrationResponseContext.java
+++ b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/messaging/context/OIDCClientRegistrationResponseContext.java
@@ -16,8 +16,11 @@ package net.shibboleth.idp.plugin.oidc.op.messaging.context;
import java.time.Instant;
+import javax.annotation.Nullable;
+
import org.opensaml.messaging.context.BaseContext;
+import com.nimbusds.openid.connect.sdk.rp.OIDCClientInformation;
import com.nimbusds.openid.connect.sdk.rp.OIDCClientMetadata;
/**
@@ -27,31 +30,34 @@ import com.nimbusds.openid.connect.sdk.rp.OIDCClientMetadata;
public class OIDCClientRegistrationResponseContext extends BaseContext {
/** Mandatory Unique Client Identifier. */
- private String clientId;
+ @Nullable private String clientId;
/** Optional client secret. */
- private String clientSecret;
+ @Nullable private String clientSecret;
/** Optional registration access token. */
- private String regAccessToken;
+ @Nullable private String regAccessToken;
/** Optional location of the client configuration endpoint. */
- private String regClientUri;
+ @Nullable private String regClientUri;
/** Optional time at which the client identifier was issued. */
- private Instant clientIdIssuedAt;
+ @Nullable private Instant clientIdIssuedAt;
/** Time at which the client secret will expire or 0 if it will not expire. Required if the secret was issued. */
- private Instant clientSecretExpiresAt;
+ @Nullable private Instant clientSecretExpiresAt;
/** The metadata for the client: the attributes supported by the OP must be included. */
- private OIDCClientMetadata clientMetadata;
+ @Nullable private OIDCClientMetadata clientMetadata;
+
+ /** The client information object carrying client ID, secret and metadata. */
+ @Nullable private OIDCClientInformation clientInformation;
/**
* Get the client identifier.
* @return The client identifier.
*/
- public String getClientId() {
+ @Nullable public String getClientId() {
return clientId;
}
@@ -59,7 +65,7 @@ public class OIDCClientRegistrationResponseContext extends BaseContext {
* Set the client identifier.
* @param id The client identifier.
*/
- public void setClientId(final String id) {
+ public void setClientId(@Nullable final String id) {
this.clientId = id;
}
@@ -67,7 +73,7 @@ public class OIDCClientRegistrationResponseContext extends BaseContext {
* Get the client secret.
* @return The client secret.
*/
- public String getClientSecret() {
+ @Nullable public String getClientSecret() {
return clientSecret;
}
@@ -75,7 +81,7 @@ public class OIDCClientRegistrationResponseContext extends BaseContext {
* Set the client secret.
* @param secret The client secret.
*/
- public void setClientSecret(final String secret) {
+ public void setClientSecret(@Nullable final String secret) {
this.clientSecret = secret;
}
@@ -83,7 +89,7 @@ public class OIDCClientRegistrationResponseContext extends BaseContext {
* Get the registration access token.
* @return The registration access token.
*/
- public String getRegAccessToken() {
+ @Nullable public String getRegAccessToken() {
return regAccessToken;
}
@@ -91,7 +97,7 @@ public class OIDCClientRegistrationResponseContext extends BaseContext {
* Set the registration access token.
* @param accessToken The registration access token.
*/
- public void setRegAccessToken(final String accessToken) {
+ public void setRegAccessToken(@Nullable final String accessToken) {
this.regAccessToken = accessToken;
}
@@ -99,7 +105,7 @@ public class OIDCClientRegistrationResponseContext extends BaseContext {
* Get the location of the client configuration endpoint.
* @return The location of the client configuration endpoint.
*/
- public String getRegClientUri() {
+ @Nullable public String getRegClientUri() {
return regClientUri;
}
@@ -107,7 +113,7 @@ public class OIDCClientRegistrationResponseContext extends BaseContext {
* Set the location of the client configuration endpoint.
* @param clientUri The location of the client configuration endpoint.
*/
- public void setRegClientUri(final String clientUri) {
+ public void setRegClientUri(@Nullable final String clientUri) {
this.regClientUri = clientUri;
}
@@ -115,7 +121,7 @@ public class OIDCClientRegistrationResponseContext extends BaseContext {
* Get the time at which the client identifier was issued.
* @return The time at which the client identifier was issued.
*/
- public Instant getClientIdIssuedAt() {
+ @Nullable public Instant getClientIdIssuedAt() {
return clientIdIssuedAt;
}
@@ -123,7 +129,7 @@ public class OIDCClientRegistrationResponseContext extends BaseContext {
* Set the time at which the client identifier was issued.
* @param idIssuedAt The time at which the client identifier was issued.
*/
- public void setClientIdIssuedAt(final Instant idIssuedAt) {
+ public void setClientIdIssuedAt(@Nullable final Instant idIssuedAt) {
this.clientIdIssuedAt = idIssuedAt;
}
@@ -131,7 +137,7 @@ public class OIDCClientRegistrationResponseContext extends BaseContext {
* Get the time at which the client secret will expire.
* @return The time at which the client secret will expire.
*/
- public Instant getClientSecretExpiresAt() {
+ @Nullable public Instant getClientSecretExpiresAt() {
return clientSecretExpiresAt;
}
@@ -139,7 +145,7 @@ public class OIDCClientRegistrationResponseContext extends BaseContext {
* Set the time at which the client secret will expire.
* @param secretExpiresAt The time at which the client secret will expire.
*/
- public void setClientSecretExpiresAt(final Instant secretExpiresAt) {
+ public void setClientSecretExpiresAt(@Nullable final Instant secretExpiresAt) {
this.clientSecretExpiresAt = secretExpiresAt;
}
@@ -147,7 +153,7 @@ public class OIDCClientRegistrationResponseContext extends BaseContext {
* Get the metadata for the client: the attributes supported by the OP must be included.
* @return The metadata for the client: the attributes supported by the OP must be included.
*/
- public OIDCClientMetadata getClientMetadata() {
+ @Nullable public OIDCClientMetadata getClientMetadata() {
return clientMetadata;
}
@@ -155,7 +161,29 @@ public class OIDCClientRegistrationResponseContext extends BaseContext {
* Set the metadata for the client: the attributes supported by the OP must be included.
* @param metadata The metadata for the client: the attributes supported by the OP must be included.
*/
- public void setClientMetadata(final OIDCClientMetadata metadata) {
+ public void setClientMetadata(@Nullable final OIDCClientMetadata metadata) {
this.clientMetadata = metadata;
}
+
+ /**
+ * Set the client information object carrying client ID, secret and metadata.
+ *
+ * @since 4.3.0
+ *
+ * @param information The client information object carrying client ID, secret and metadata.
+ */
+ public void setClientInformation(@Nullable final OIDCClientInformation information) {
+ clientInformation = information;
+ }
+
+ /**
+ * Get the client information object carrying client ID, secret and metadata.
+ *
+ * @since 4.3.0
+ *
+ * @return The client information object carrying client ID, secret and metadata.
+ */
+ @Nullable public OIDCClientInformation getClientInformation() {
+ return clientInformation;
+ }
}
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/messaging/context/navigate/DefaultClientInformationLookupFunction.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/messaging/context/navigate/DefaultClientInformationLookupFunction.java
new file mode 100644
index 00000000..75acdee2
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/messaging/context/navigate/DefaultClientInformationLookupFunction.java
@@ -0,0 +1,47 @@
+/*
+ * 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.messaging.context.navigate;
+
+import java.util.Objects;
+import java.util.Optional;
+import java.util.function.Function;
+
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import com.nimbusds.openid.connect.sdk.rp.OIDCClientInformation;
+
+import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCClientRegistrationResponseContext;
+
+/**
+ * Default lookup function for fetching {@link OIDCClientInformation} from {@link OIDCClientRegistrationResponseContext}
+ * located under outbound message context.
+ *
+ * @since 4.3.0
+ */
+public class DefaultClientInformationLookupFunction implements Function<ProfileRequestContext, OIDCClientInformation> {
+
+ /** {@inheritDoc} */
+ @Nullable public OIDCClientInformation apply(@Nullable final ProfileRequestContext input) {
+ return Optional.ofNullable(input)
+ .map(profileRequestContext -> profileRequestContext.getOutboundMessageContext())
+ .filter(Objects::nonNull)
+ .map(messageContext -> messageContext.getSubcontext(OIDCClientRegistrationResponseContext.class))
+ .filter(Objects::nonNull)
+ .map(oidcResponseContext -> oidcResponseContext.getClientInformation())
+ .orElse(null);
+ }
+}
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/messaging/context/navigate/DefaultRequestedMetadataLookupFunction.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/messaging/context/navigate/DefaultRequestedMetadataLookupFunction.java
new file mode 100644
index 00000000..655cd40a
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/messaging/context/navigate/DefaultRequestedMetadataLookupFunction.java
@@ -0,0 +1,48 @@
+/*
+ * 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.messaging.context.navigate;
+
+import java.util.Objects;
+import java.util.Optional;
+import java.util.function.Function;
+
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import com.nimbusds.openid.connect.sdk.rp.OIDCClientMetadata;
+import com.nimbusds.openid.connect.sdk.rp.OIDCClientRegistrationRequest;
+
+/**
+ * Default lookup function for fetching requested {@link OIDCClientMetadata} from the inbound dynamic client
+ * registration request message ({@link OIDCClientRegistrationRequest}).
+ *
+ * @since 4.3.0
+ */
+public class DefaultRequestedMetadataLookupFunction implements Function<ProfileRequestContext, OIDCClientMetadata> {
+
+ /** {@inheritDoc} */
+ @Nullable public OIDCClientMetadata apply(@Nullable final ProfileRequestContext input) {
+ return Optional.ofNullable(input)
+ .map(profileRequestContext -> profileRequestContext.getInboundMessageContext())
+ .filter(Objects::nonNull)
+ .map(messageContext -> messageContext.getMessage())
+ .filter(OIDCClientRegistrationRequest.class::isInstance)
+ .map(OIDCClientRegistrationRequest.class::cast)
+ .map(request -> request.getOIDCClientMetadata())
+ .orElse(null);
+
+ }
+}
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/config/DefaultOIDFederationAutomaticRegistrationConfiguration.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/config/AbstractOIDFederationRegistrationProfileConfiguration.java
similarity index 80%
copy from idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/config/DefaultOIDFederationAutomaticRegistrationConfiguration.java
copy to idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/config/AbstractOIDFederationRegistrationProfileConfiguration.java
index 42c52a7b..219e6d61 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/config/DefaultOIDFederationAutomaticRegistrationConfiguration.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/config/AbstractOIDFederationRegistrationProfileConfiguration.java
@@ -1,16 +1,3 @@
-/*
- * 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.config;
@@ -26,6 +13,7 @@ import org.opensaml.profile.context.ProfileRequestContext;
import net.shibboleth.oidc.metadata.policy.MetadataPolicy;
import net.shibboleth.profile.config.AbstractConditionalProfileConfiguration;
+import net.shibboleth.shared.annotation.ParameterName;
import net.shibboleth.shared.annotation.constraint.NonnullElements;
import net.shibboleth.shared.annotation.constraint.NotEmpty;
import net.shibboleth.shared.annotation.constraint.NotLive;
@@ -36,14 +24,10 @@ import net.shibboleth.shared.logic.Constraint;
import net.shibboleth.shared.logic.FunctionSupport;
/**
- * Implementation of a profile configuration for the OpenID Federation Automatic Registration.
+ * Abstract implementation class for profile configurations related OpenID Federation client registration.
*/
-public class DefaultOIDFederationAutomaticRegistrationConfiguration extends AbstractConditionalProfileConfiguration
- implements OIDFederationAutomaticRegistrationConfiguration {
-
- /** OIDC provider information profile counter name. */
- @Nonnull @NotEmpty
- public static final String PROFILE_COUNTER = "net.shibboleth.idp.profiles.oidfed.automaticregistration";
+public class AbstractOIDFederationRegistrationProfileConfiguration extends AbstractConditionalProfileConfiguration
+ implements OIDFederationRegistrationProfileConfiguration {
/** Lookup function to local metadata policy to be merged into the federation policy. */
@Nonnull private Function<ProfileRequestContext,Map<String, MetadataPolicy>> localMetadataPolicyLookupStrategy;
@@ -56,18 +40,12 @@ public class DefaultOIDFederationAutomaticRegistrationConfiguration extends Abst
/**
* Constructor.
+ *
+ * @param id ID of the communication profile, never null or empty
*/
- public DefaultOIDFederationAutomaticRegistrationConfiguration() {
- this(PROFILE_ID);
- }
-
- /**
- * Creates a new configuration instance.
- *
- * @param profileId Unique profile identifier.
- */
- public DefaultOIDFederationAutomaticRegistrationConfiguration(@Nonnull @NotEmpty final String profileId) {
- super(profileId);
+ public AbstractOIDFederationRegistrationProfileConfiguration(
+ @Nonnull @NotEmpty @ParameterName(name="id") final String id) {
+ super(id);
localMetadataPolicyLookupStrategy = FunctionSupport.constant(CollectionSupport.emptyMap());
mandatoryTrustMarksLookupStrategy = FunctionSupport.constant(CollectionSupport.emptyList());
maximumTrustMarkLifetimeLookupStrategy = FunctionSupport.constant(Duration.ofDays(365));
@@ -169,4 +147,4 @@ public class DefaultOIDFederationAutomaticRegistrationConfiguration extends Abst
@Nullable final Function<ProfileRequestContext,Duration> strategy) {
maximumTrustMarkLifetimeLookupStrategy = Constraint.isNotNull(strategy, "Lookup strategy cannot be null");
}
-}
\ No newline at end of file
+}
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/config/DefaultOIDFederationAutomaticRegistrationConfiguration.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/config/DefaultOIDFederationAutomaticRegistrationConfiguration.java
index 42c52a7b..01bc35d5 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/config/DefaultOIDFederationAutomaticRegistrationConfiguration.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/config/DefaultOIDFederationAutomaticRegistrationConfiguration.java
@@ -14,46 +14,21 @@
package net.shibboleth.idp.plugin.oidc.op.oidfed.config;
-import java.time.Duration;
-import java.util.List;
-import java.util.Map;
-import java.util.function.Function;
-
import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import org.opensaml.profile.context.ProfileRequestContext;
-import net.shibboleth.oidc.metadata.policy.MetadataPolicy;
-import net.shibboleth.profile.config.AbstractConditionalProfileConfiguration;
-import net.shibboleth.shared.annotation.constraint.NonnullElements;
import net.shibboleth.shared.annotation.constraint.NotEmpty;
-import net.shibboleth.shared.annotation.constraint.NotLive;
-import net.shibboleth.shared.annotation.constraint.Positive;
-import net.shibboleth.shared.annotation.constraint.Unmodifiable;
-import net.shibboleth.shared.collection.CollectionSupport;
-import net.shibboleth.shared.logic.Constraint;
-import net.shibboleth.shared.logic.FunctionSupport;
/**
* Implementation of a profile configuration for the OpenID Federation Automatic Registration.
*/
-public class DefaultOIDFederationAutomaticRegistrationConfiguration extends AbstractConditionalProfileConfiguration
- implements OIDFederationAutomaticRegistrationConfiguration {
+public class DefaultOIDFederationAutomaticRegistrationConfiguration
+ extends AbstractOIDFederationRegistrationProfileConfiguration
+ implements OIDFederationAutomaticRegistrationConfiguration {
/** OIDC provider information profile counter name. */
@Nonnull @NotEmpty
public static final String PROFILE_COUNTER = "net.shibboleth.idp.profiles.oidfed.automaticregistration";
- /** Lookup function to local metadata policy to be merged into the federation policy. */
- @Nonnull private Function<ProfileRequestContext,Map<String, MetadataPolicy>> localMetadataPolicyLookupStrategy;
-
- /** Lookup function to mandatory trust marks. */
- @Nonnull private Function<ProfileRequestContext,List<String>> mandatoryTrustMarksLookupStrategy;
-
- /** Lookup function to supply maximum trust mark lifetime. */
- @Nonnull private Function<ProfileRequestContext,Duration> maximumTrustMarkLifetimeLookupStrategy;
-
/**
* Constructor.
*/
@@ -68,105 +43,5 @@ public class DefaultOIDFederationAutomaticRegistrationConfiguration extends Abst
*/
public DefaultOIDFederationAutomaticRegistrationConfiguration(@Nonnull @NotEmpty final String profileId) {
super(profileId);
- localMetadataPolicyLookupStrategy = FunctionSupport.constant(CollectionSupport.emptyMap());
- mandatoryTrustMarksLookupStrategy = FunctionSupport.constant(CollectionSupport.emptyList());
- maximumTrustMarkLifetimeLookupStrategy = FunctionSupport.constant(Duration.ofDays(365));
- }
-
- /** {@inheritDoc} */
- @Override @Nonnull @NonnullElements @NotLive @Unmodifiable
- public Map<String, MetadataPolicy> getLocalMetadataPolicy(
- @Nullable final ProfileRequestContext profileRequestContext) {
- final Map<String, MetadataPolicy> policy = localMetadataPolicyLookupStrategy.apply(profileRequestContext);
- if (policy != null) {
- return CollectionSupport.copyToMap(policy);
- }
- return CollectionSupport.emptyMap();
- }
-
- /**
- * Set local metadata policy to be merged into the federation policy.
- *
- * @param policy metadata policy
- */
- public void setLocalMetadataPolicy(
- @Nonnull @NonnullElements @NotLive @Unmodifiable final Map<String, MetadataPolicy> policy) {
- localMetadataPolicyLookupStrategy = FunctionSupport.constant(policy);
- }
-
- /**
- * Sets lookup strategy for local metadata policy to be merged into the federation policy.
- *
- * @param strategy lookup strategy
- */
- public void setLocalMetadataPolicyLookupStrategy(
- @Nonnull final Function<ProfileRequestContext,Map<String, MetadataPolicy>> strategy) {
- localMetadataPolicyLookupStrategy =
- Constraint.isNotNull(strategy, "Local metadata policy lookup strategy cannot be null");
- }
-
- /** {@inheritDoc} */
- @Override @Nonnull @NonnullElements @NotLive @Unmodifiable
- public List<String> getMandatoryTrustMarks(@Nullable final ProfileRequestContext profileRequestContext) {
- final List<String> trustMarks = mandatoryTrustMarksLookupStrategy.apply(profileRequestContext);
- if (trustMarks != null) {
- return CollectionSupport.copyToList(trustMarks);
- }
- return CollectionSupport.emptyList();
- }
-
- /**
- * Set mandatory trust marks.
- *
- * @param marks trust marks
- */
- public void setMandatoryTrustMarks(@Nonnull @NonnullElements @NotLive @Unmodifiable final List<String> marks) {
- mandatoryTrustMarksLookupStrategy = FunctionSupport.constant(marks);
- }
-
- /**
- * Sets lookup strategy for mandatory trust marks value.
- *
- * @param strategy lookup strategy
- */
- public void setMandatoryTrustMarksLookupStrategy(
- @Nonnull final Function<ProfileRequestContext,List<String>> strategy) {
- mandatoryTrustMarksLookupStrategy =
- Constraint.isNotNull(strategy, "Mandatory trust marks lookup strategy cannot be null");
- }
-
- /** {@inheritDoc} */
- @Override
- @Positive @Nonnull
- public Duration getMaximumTrustMarkLifetime(@Nullable final ProfileRequestContext profileRequestContext) {
- final Duration lifetime = maximumTrustMarkLifetimeLookupStrategy.apply(profileRequestContext);
-
- Constraint.isTrue(lifetime != null && !lifetime.isZero() && !lifetime.isNegative(),
- "Maximum trust mark lifetime must be greater than 0");
- assert lifetime != null;
- return lifetime;
- }
-
- /**
- * Set the maximum lifetime of a trust mark.
- *
- * @param lifetime lifetime of a trust mark
- */
- public void setMaximumTrustMarkLifetime(@Positive @Nonnull final Duration lifetime) {
- final Duration trustMarkLifetime = Constraint.isNotNull(lifetime, "Maximum trust mark lifetime cannot be null");
- Constraint.isTrue(!trustMarkLifetime.isZero() && !trustMarkLifetime.isNegative(),
- "Maximum trust mark lifetime must be greater than 0");
-
- maximumTrustMarkLifetimeLookupStrategy = FunctionSupport.constant(trustMarkLifetime);
- }
-
- /**
- * Set a lookup strategy for the maximum trust mark lifetime.
- *
- * @param strategy lookup strategy
- */
- public void setMaximumTrustMarkLifetimeLookupStrategy(
- @Nullable final Function<ProfileRequestContext,Duration> strategy) {
- maximumTrustMarkLifetimeLookupStrategy = Constraint.isNotNull(strategy, "Lookup strategy cannot be null");
}
}
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/config/DefaultOIDFederationExplicitRegistrationConfiguration.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/config/DefaultOIDFederationExplicitRegistrationConfiguration.java
new file mode 100644
index 00000000..4cacb53a
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/config/DefaultOIDFederationExplicitRegistrationConfiguration.java
@@ -0,0 +1,195 @@
+/*
+ * 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.config;
+
+import java.util.Collection;
+import java.util.Set;
+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 com.nimbusds.oauth2.sdk.auth.ClientAuthenticationMethod;
+
+import net.shibboleth.shared.annotation.constraint.NonnullElements;
+import net.shibboleth.shared.annotation.constraint.NotEmpty;
+import net.shibboleth.shared.annotation.constraint.NotLive;
+import net.shibboleth.shared.annotation.constraint.Unmodifiable;
+import net.shibboleth.shared.collection.CollectionSupport;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.logic.FunctionSupport;
+import net.shibboleth.shared.logic.PredicateSupport;
+import net.shibboleth.shared.primitive.StringSupport;
+
+/**
+ * Implementation of a profile configuration for the OpenID Federation Explicit Registration.
+ */
+public class DefaultOIDFederationExplicitRegistrationConfiguration
+ extends AbstractOIDFederationRegistrationProfileConfiguration
+ implements OIDFederationExplicitRegistrationConfiguration {
+
+ /** OIDC provider information profile counter name. */
+ @Nonnull @NotEmpty
+ public static final String PROFILE_COUNTER = "net.shibboleth.idp.profiles.oidfed.explicitregistration";
+
+ /** Predicate used to indicate whether authorization code flow is supported by this profile. Default true. */
+ @Nonnull private Predicate<ProfileRequestContext> authorizationCodeFlowPredicate;
+
+ /** Predicate used to indicate whether implicit flow is supported by this profile. Default true. */
+ @Nonnull private Predicate<ProfileRequestContext> implicitFlowPredicate;
+
+ /** Predicate used to indicate whether refresh tokens are supported by this profile. Default true. */
+ @Nonnull private Predicate<ProfileRequestContext> refreshTokensPredicate;
+
+ /** Enabled token endpoint authentication methods. */
+ @Nonnull private Function<ProfileRequestContext,Set<String>> tokenEndpointAuthMethodsLookupStrategy;
+
+ /**
+ * Constructor.
+ */
+ public DefaultOIDFederationExplicitRegistrationConfiguration() {
+ this(PROFILE_ID);
+ }
+
+ /**
+ * Creates a new configuration instance.
+ *
+ * @param profileId Unique profile identifier.
+ */
+ public DefaultOIDFederationExplicitRegistrationConfiguration(@Nonnull @NotEmpty final String profileId) {
+ super(profileId);
+ authorizationCodeFlowPredicate = PredicateSupport.alwaysTrue();
+ implicitFlowPredicate = PredicateSupport.alwaysTrue();
+ refreshTokensPredicate = PredicateSupport.alwaysTrue();
+ tokenEndpointAuthMethodsLookupStrategy = FunctionSupport.constant(
+ CollectionSupport.setOf(
+ ClientAuthenticationMethod.CLIENT_SECRET_BASIC.toString(),
+ ClientAuthenticationMethod.CLIENT_SECRET_POST.toString(),
+ ClientAuthenticationMethod.CLIENT_SECRET_JWT.toString(),
+ ClientAuthenticationMethod.PRIVATE_KEY_JWT.toString()));
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isAuthorizationCodeFlowEnabled(@Nullable final ProfileRequestContext profileRequestContext) {
+ return authorizationCodeFlowPredicate.test(profileRequestContext);
+ }
+
+ /**
+ * Set whether authorization code flow is supported by this profile.
+ *
+ * @param flag flag to set
+ */
+ public void setAuthorizationCodeFlowEnabled(final boolean flag) {
+ authorizationCodeFlowPredicate = flag ? PredicateSupport.alwaysTrue() : PredicateSupport.alwaysFalse();
+ }
+
+ /**
+ * Set condition used to indicate whether authorization code flow is supported by this profile.
+ *
+ * @param condition condition to set
+ */
+ public void setAuthorizationCodeFlowEnabledPredicate(@Nonnull final Predicate<ProfileRequestContext> condition) {
+ authorizationCodeFlowPredicate = Constraint.isNotNull(condition,
+ "Condition used to indicate whether authorization code flow is supported cannot be null");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isImplicitFlowEnabled(@Nullable final ProfileRequestContext profileRequestContext) {
+ return implicitFlowPredicate.test(profileRequestContext);
+ }
+
+ /**
+ * Set whether hybrid flow is supported by this profile.
+ *
+ * @param flag flag to set
+ */
+ public void setImplicitFlowEnabled(final boolean flag) {
+ implicitFlowPredicate = flag ? PredicateSupport.alwaysTrue() : PredicateSupport.alwaysFalse();
+ }
+
+ /**
+ * Set condition used to indicate whether hybrid flow is supported by this profile.
+ *
+ * @param condition condition to set.
+ */
+ public void setImplicitFlowEnabledPredicate(@Nonnull final Predicate<ProfileRequestContext> condition) {
+ implicitFlowPredicate = Constraint.isNotNull(condition,
+ "Condition used to indicate whether hybrid flow is supported cannot be null");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isRefreshTokensEnabled(@Nullable final ProfileRequestContext profileRequestContext) {
+ return refreshTokensPredicate.test(profileRequestContext);
+ }
+
+ /**
+ * Set whether refresh tokens are supported by this profile.
+ *
+ * @param flag flag to set
+ */
+ public void setRefreshTokensEnabled(final boolean flag) {
+ refreshTokensPredicate = flag ? PredicateSupport.alwaysTrue() : PredicateSupport.alwaysFalse();
+ }
+
+ /**
+ * Set condition used to indicate whether refresh tokens are supported by this profile.
+ *
+ * @param condition condition to set
+ */
+ public void setRefreshTokensEnabledPredicate(@Nonnull final Predicate<ProfileRequestContext> condition) {
+ refreshTokensPredicate = Constraint.isNotNull(condition,
+ "Condition used to indicate whether refresh tokens are supported cannot be null");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ @Nonnull @NonnullElements @NotLive @Unmodifiable public Set<String> getTokenEndpointAuthMethods(
+ @Nullable final ProfileRequestContext profileRequestContext) {
+
+ final Collection<String> methods = tokenEndpointAuthMethodsLookupStrategy.apply(profileRequestContext);
+ if (methods != null) {
+ return CollectionSupport.copyToSet(methods);
+ }
+ return CollectionSupport.emptySet();
+ }
+
+ /**
+ * Set the enabled token endpoint authentication methods.
+ *
+ * @param methods What to set.
+ */
+ public void setTokenEndpointAuthMethods(@Nonnull @NonnullElements final Collection<String> methods) {
+ Constraint.isNotNull(methods, "Collection of methods cannot be null");
+
+ tokenEndpointAuthMethodsLookupStrategy =
+ FunctionSupport.constant(Set.copyOf(StringSupport.normalizeStringCollection(methods)));
+ }
+
+ /**
+ * Set a lookup strategy for the enabled token endpoint authentication methods.
+ *
+ * @param strategy lookup strategy
+ */
+ public void setTokenEndpointAuthMethodsLookupStrategy(
+ @Nonnull final Function<ProfileRequestContext,Set<String>> strategy) {
+ tokenEndpointAuthMethodsLookupStrategy = Constraint.isNotNull(strategy, "Lookup strategy cannot be null");
+ }
+}
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/config/OIDFederationAutomaticRegistrationConfiguration.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/config/OIDFederationAutomaticRegistrationConfiguration.java
index d396684b..fe07f232 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/config/OIDFederationAutomaticRegistrationConfiguration.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/config/OIDFederationAutomaticRegistrationConfiguration.java
@@ -14,69 +14,12 @@
package net.shibboleth.idp.plugin.oidc.op.oidfed.config;
-import java.time.Duration;
-import java.util.List;
-import java.util.Map;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import org.opensaml.profile.context.ProfileRequestContext;
-
-import net.shibboleth.oidc.metadata.policy.MetadataPolicy;
-import net.shibboleth.oidc.profile.config.OIDCProfileConfiguration;
-import net.shibboleth.shared.annotation.ConfigurationSetting;
-import net.shibboleth.shared.annotation.constraint.NonnullElements;
-import net.shibboleth.shared.annotation.constraint.NotLive;
-import net.shibboleth.shared.annotation.constraint.Positive;
-import net.shibboleth.shared.annotation.constraint.Unmodifiable;
-
/**
* Profile configuration for an OpenID Federation Automatic Registration.
*/
-public interface OIDFederationAutomaticRegistrationConfiguration extends OIDCProfileConfiguration {
-
- /** OIDC base protocol URI. Sections 4 and 11 are the most relevant. */
- public static final String PROTOCOL_URI = "https://openid.net/specs/openid-federation-1_0.html";
+public interface OIDFederationAutomaticRegistrationConfiguration extends OIDFederationRegistrationProfileConfiguration {
/** ID for this profile configuration. */
public static final String PROFILE_ID = "http://shibboleth.net/ns/profiles/oidfed/automaticregistration";
- /**
- * Get local metadata policy to be merged into the federation policy.
- *
- * <p>Defaults to empty map.</p>
- *
- * @param profileRequestContext profile request context
- *
- * @return local metadata policy
- */
- @ConfigurationSetting(name="localMetadataPolicy")
- @Nonnull @NonnullElements @NotLive @Unmodifiable
- Map<String, MetadataPolicy> getLocalMetadataPolicy(@Nullable final ProfileRequestContext profileRequestContext);
-
- /**
- * Get the mandatory trust mark identifiers required by this profile configuration.
- *
- * @param profileRequestContext profile request context
- *
- * @return mandatory trust mark identifiers
- */
- @ConfigurationSetting(name="mandatoryTrustMarks")
- @Nonnull @NonnullElements @NotLive @Unmodifiable List<String> getMandatoryTrustMarks(
- @Nullable final ProfileRequestContext profileRequestContext);
-
- /**
- * Get maximum lifetime for trust marks.
- *
- * <p>Defaults to one year.</p>
- *
- * @param profileRequestContext profile request context
- *
- * @return maximum lifetime
- */
- @ConfigurationSetting(name="maximumTrustMarkLifetime")
- @Positive @Nonnull
- Duration getMaximumTrustMarkLifetime(@Nullable final ProfileRequestContext profileRequestContext);
-
}
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/config/OIDFederationExplicitRegistrationConfiguration.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/config/OIDFederationExplicitRegistrationConfiguration.java
new file mode 100644
index 00000000..7efcc2ea
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/config/OIDFederationExplicitRegistrationConfiguration.java
@@ -0,0 +1,50 @@
+/*
+ * 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.config;
+
+import java.util.Set;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.oidc.profile.oauth2.config.OAuth2FlowAwareProfileConfiguration;
+import net.shibboleth.shared.annotation.ConfigurationSetting;
+import net.shibboleth.shared.annotation.constraint.NonnullElements;
+import net.shibboleth.shared.annotation.constraint.NotLive;
+import net.shibboleth.shared.annotation.constraint.Unmodifiable;
+
+/**
+ * Profile configuration for an OpenID Federation Explicit Registration.
+ */
+public interface OIDFederationExplicitRegistrationConfiguration
+ extends OIDFederationRegistrationProfileConfiguration, OAuth2FlowAwareProfileConfiguration {
+
+ /** ID for this profile configuration. */
+ public static final String PROFILE_ID = "http://shibboleth.net/ns/profiles/oidfed/explicitregistration";
+
+ /**
+ * Get the enabled token endpoint authentication methods.
+ *
+ * @param profileRequestContext profile request context
+ *
+ * @return enabled token endpoint authentication methods
+ */
+ @ConfigurationSetting(name="tokenEndpointAuthMethods")
+ @Nonnull @NonnullElements @NotLive @Unmodifiable Set<String> getTokenEndpointAuthMethods(
+ @Nullable final ProfileRequestContext profileRequestContext);
+
+}
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/config/OIDFederationAutomaticRegistrationConfiguration.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/config/OIDFederationRegistrationProfileConfiguration.java
similarity index 66%
copy from idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/config/OIDFederationAutomaticRegistrationConfiguration.java
copy to idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/config/OIDFederationRegistrationProfileConfiguration.java
index d396684b..98277cc9 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/config/OIDFederationAutomaticRegistrationConfiguration.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/config/OIDFederationRegistrationProfileConfiguration.java
@@ -1,16 +1,3 @@
-/*
- * 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.config;
@@ -24,23 +11,20 @@ import javax.annotation.Nullable;
import org.opensaml.profile.context.ProfileRequestContext;
import net.shibboleth.oidc.metadata.policy.MetadataPolicy;
-import net.shibboleth.oidc.profile.config.OIDCProfileConfiguration;
+import net.shibboleth.oidc.profile.oauth2.config.OAuth2ProfileConfiguration;
import net.shibboleth.shared.annotation.ConfigurationSetting;
import net.shibboleth.shared.annotation.constraint.NonnullElements;
import net.shibboleth.shared.annotation.constraint.NotLive;
import net.shibboleth.shared.annotation.constraint.Positive;
import net.shibboleth.shared.annotation.constraint.Unmodifiable;
-/**
- * Profile configuration for an OpenID Federation Automatic Registration.
+/**
+ * Profile configuration for an OpenID Federation profiles related to client registration.
*/
-public interface OIDFederationAutomaticRegistrationConfiguration extends OIDCProfileConfiguration {
-
- /** OIDC base protocol URI. Sections 4 and 11 are the most relevant. */
- public static final String PROTOCOL_URI = "https://openid.net/specs/openid-federation-1_0.html";
+public interface OIDFederationRegistrationProfileConfiguration extends OAuth2ProfileConfiguration {
- /** ID for this profile configuration. */
- public static final String PROFILE_ID = "http://shibboleth.net/ns/profiles/oidfed/automaticregistration";
+ /** OpenID Federation base protocol URI. */
+ public static final String PROTOCOL_URI = "https://openid.net/specs/openid-federation-1_0.html";
/**
* Get local metadata policy to be merged into the federation policy.
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/config/TokenEndpointAuthMethodLookupFunction.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/config/TokenEndpointAuthMethodLookupFunction.java
new file mode 100644
index 00000000..286349ee
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/config/TokenEndpointAuthMethodLookupFunction.java
@@ -0,0 +1,55 @@
+/*
+ * 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.config;
+
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import com.nimbusds.oauth2.sdk.auth.ClientAuthenticationMethod;
+
+import net.shibboleth.profile.context.RelyingPartyContext;
+import net.shibboleth.profile.context.navigate.AbstractRelyingPartyLookupFunction;
+
+/**
+ * A function that obtains
+ * {@link OIDFederationExplicitRegistrationConfiguration#getTokenEndpointAuthMethods(ProfileRequestContext)}
+ * if such a profile is available from a {@link RelyingPartyContext} obtained via a lookup function,
+ * by default a child of the {@link ProfileRequestContext}. That result is then transformed into a list
+ * of {@link ClientAuthenticationMethod}s.
+ *
+ * <p>If a specific setting is unavailable, a null value is returned.</p>
+ */
+public class TokenEndpointAuthMethodLookupFunction
+ extends AbstractRelyingPartyLookupFunction<Set<ClientAuthenticationMethod>> {
+
+ /** {@inheritDoc} */
+ @Override
+ @Nullable public Set<ClientAuthenticationMethod> apply(@Nullable final ProfileRequestContext input) {
+ return Optional.ofNullable(getRelyingPartyContextLookupStrategy().apply(input))
+ .map(relyingPartyContext -> relyingPartyContext.getProfileConfig())
+ .filter(OIDFederationExplicitRegistrationConfiguration.class::isInstance)
+ .map(OIDFederationExplicitRegistrationConfiguration.class::cast)
+ .map(config -> config.getTokenEndpointAuthMethods(input).stream()
+ .map(ClientAuthenticationMethod::new)
+ .collect(Collectors.toUnmodifiableSet()))
+ .orElse(null);
+ }
+
+}
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/decoding/impl/ExplicitClientRegistrationRequestDecoder.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/decoding/impl/ExplicitClientRegistrationRequestDecoder.java
new file mode 100644
index 00000000..da8276e8
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/decoding/impl/ExplicitClientRegistrationRequestDecoder.java
@@ -0,0 +1,154 @@
+/*
+ * 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.decoding.impl;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.List;
+import java.util.Objects;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.messaging.decoder.MessageDecodingException;
+import org.slf4j.Logger;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.base.MoreObjects;
+import com.nimbusds.oauth2.sdk.ParseException;
+import com.nimbusds.oauth2.sdk.http.HTTPRequest;
+import com.nimbusds.oauth2.sdk.http.JakartaServletUtils;
+import com.nimbusds.openid.connect.sdk.federation.entities.EntityStatement;
+
+import jakarta.servlet.http.HttpServletRequest;
+import net.shibboleth.idp.plugin.oidc.op.decoding.impl.RequestUtil;
+import net.shibboleth.idp.plugin.oidc.op.oauth2.decoding.impl.BaseOAuth2RequestDecoder;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.messaging.impl.ExplicitClientRegistrationRequest;
+import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.shared.component.ComponentInitializationException;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.primitive.LoggerFactory;
+
+/**
+ * Message decoder decoding OpenID Federation Explicit Registration request {@link ExplicitClientRegistrationRequest}.
+ *
+ * @since 4.3.0
+ */
+public class ExplicitClientRegistrationRequestDecoder extends BaseOAuth2RequestDecoder<ExplicitClientRegistrationRequest> {
+
+ /** Class logger. */
+ @Nonnull
+ private final Logger log = LoggerFactory.getLogger(ExplicitClientRegistrationRequestDecoder.class);
+
+ /** Object mapper used for pretty-printing JSON in the request and decoding trust chain from the request. */
+ @NonnullAfterInit private ObjectMapper objectMapper;
+
+ /**
+ * Set the object mapper used for pretty-printing JSON in the request and decoding trust chain from the request.
+ *
+ * @param mapper What to set.
+ */
+ public void setObjectMapper(@Nonnull final ObjectMapper mapper) {
+ checkSetterPreconditions();
+ objectMapper = Constraint.isNotNull(mapper, "Object mapper cannot be null");
+ }
+
+ /** {@inheritDoc} */
+ protected void doInitialize() throws ComponentInitializationException {
+ super.doInitialize();
+
+ if (objectMapper == null) {
+ throw new ComponentInitializationException("Object mapper cannot be null");
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected ExplicitClientRegistrationRequest parseMessage() throws MessageDecodingException {
+ final HttpServletRequest request = getHttpServletRequest();
+ assert request != null;
+ if (!"POST".equalsIgnoreCase(request.getMethod())) {
+ throw new MessageDecodingException("This message decoder only supports the HTTP POST method");
+ }
+ try {
+ final HTTPRequest httpRequest = JakartaServletUtils.createHTTPRequest(request);
+ getProtocolMessageLogger().trace("Inbound request {}", RequestUtil.toString(httpRequest, objectMapper));
+ final URI uri = httpRequest.getURI();
+ if (uri == null) {
+ throw new MessageDecodingException("Could not parse request URI");
+ }
+ final String contentType = request.getContentType();
+ if ("application/entity-statement+jwt".equals(contentType)) {
+ final EntityStatement entityConfiguration = deserializeEntityStatement(httpRequest.getQuery());
+ if (entityConfiguration == null) {
+ throw new MessageDecodingException("Could not deserialize entity configuration");
+ }
+ log.trace("Entity configuration claims set: {}", entityConfiguration.getClaimsSet());
+ return new ExplicitClientRegistrationRequest(uri, entityConfiguration);
+ } else if ("application/trust-chain+json".equals(contentType)) {
+ final TypeReference<List<String>> typeReference = new TypeReference<List<String>>() {};
+ final List<String> strings = objectMapper.readValue(httpRequest.getQuery(), typeReference);
+ final List<EntityStatement> trustChain = strings.stream()
+ .map(string -> deserializeEntityStatement(string))
+ .filter(Objects::nonNull)
+ .toList();
+ if (trustChain == null || trustChain.isEmpty()) {
+ throw new MessageDecodingException("Could not deserialize trust chain");
+ }
+ return new ExplicitClientRegistrationRequest(uri, trustChain);
+ } else {
+ log.warn("Unexpected content type {}", contentType);
+ throw new MessageDecodingException("Unexpected content type: " + contentType);
+ }
+ } catch (final IOException e) {
+ log.error("Could not create HTTP request from the request", e);
+ throw new MessageDecodingException(e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected String getMessageToLog(@Nullable final ExplicitClientRegistrationRequest message) {
+ return message == null ? null : MoreObjects.toStringHelper(this).omitNullValues()
+ .add("entityConfiguration", getSerializedEntityStatement(message.getEntityConfiguration()))
+ .add("trustChain", getSerializedTrustChain(message.getTrustChain()))
+ .add("endpointURI", getEndpointURI(message))
+ .toString();
+ }
+
+ @Nullable protected EntityStatement deserializeEntityStatement(@Nullable final String serialized) {
+ try {
+ if (serialized != null) {
+ return EntityStatement.parse(serialized);
+ }
+ } catch (final ParseException e) {
+ log.trace("Could not construct entity statement from {}", serialized, e);
+ }
+ log.warn("Could not deserialize entity statement {}", serialized);
+ return null;
+ }
+
+ @Nullable
+ protected String getSerializedTrustChain(@Nullable final List<EntityStatement> trustChain) {
+ return trustChain == null ? null :
+ String.join(",", trustChain.stream().map(es -> es.getSignedStatement().serialize()).toList());
+ }
+
+ @Nullable
+ protected String getSerializedEntityStatement(@Nullable final EntityStatement entityStatement) {
+ return entityStatement == null ? null : entityStatement.getSignedStatement().serialize();
+ }
+}
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/messaging/impl/ExplicitClientRegistrationRequest.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/messaging/impl/ExplicitClientRegistrationRequest.java
new file mode 100644
index 00000000..5a9d9787
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/messaging/impl/ExplicitClientRegistrationRequest.java
@@ -0,0 +1,100 @@
+/*
+ * 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.messaging.impl;
+
+import java.net.URI;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import com.nimbusds.oauth2.sdk.Request;
+import com.nimbusds.oauth2.sdk.http.HTTPRequest;
+import com.nimbusds.openid.connect.sdk.federation.entities.EntityStatement;
+
+import net.shibboleth.shared.annotation.constraint.NotEmpty;
+import net.shibboleth.shared.logic.Constraint;
+
+/**
+ * Request message to the OpenID federation explicit registration endpoint.
+ */
+public class ExplicitClientRegistrationRequest implements Request {
+
+ /** The endpoint URI of the request. */
+ @Nonnull private final URI endpointUri;
+
+ /** The entity configuration from the request. */
+ @Nullable private final EntityStatement entityConfiguration;
+
+ /** The trust chain from the request. */
+ @Nullable private final List<EntityStatement> trustChain;
+
+ /**
+ * Constructor.
+ *
+ * @param uri endpoint URI
+ * @param configuration client configuration
+ */
+ public ExplicitClientRegistrationRequest(@Nonnull final URI uri, @Nonnull final EntityStatement configuration) {
+ endpointUri = Constraint.isNotNull(uri, "Endpoint URI cannot be null");
+ entityConfiguration = Constraint.isNotNull(configuration, "Entity configuration cannot be null");
+ trustChain = null;
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param uri endpoint URI
+ * @param chain trust chain
+ */
+ public ExplicitClientRegistrationRequest(@Nonnull final URI uri,
+ @Nonnull @NotEmpty final List<EntityStatement> chain) {
+ endpointUri = Constraint.isNotNull(uri, "Endpoint URI cannot be null");
+ Constraint.isNotEmpty(chain, "Trust chain cannot be empty");
+ trustChain = chain;
+ entityConfiguration = null;
+ }
+
+ /** {@inheritDoc} */
+ @Override @Nonnull
+ public URI getEndpointURI() {
+ return endpointUri;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public HTTPRequest toHTTPRequest() {
+ //TODO
+ return null;
+ }
+
+ /**
+ * Get the entity configuration from the request.
+ *
+ * @return entity configuration
+ */
+ @Nullable public EntityStatement getEntityConfiguration() {
+ return entityConfiguration;
+ }
+
+ /**
+ * Get the trust chain from the request.
+ *
+ * @return trust chain
+ */
+ @Nullable public List<EntityStatement> getTrustChain() {
+ return trustChain;
+ }
+}
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/messaging/impl/ExplicitClientRegistrationResponse.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/messaging/impl/ExplicitClientRegistrationResponse.java
new file mode 100644
index 00000000..827e6d5d
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/messaging/impl/ExplicitClientRegistrationResponse.java
@@ -0,0 +1,97 @@
+/*
+ * 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.messaging.impl;
+
+import javax.annotation.Nonnull;
+
+import com.nimbusds.jwt.SignedJWT;
+import com.nimbusds.oauth2.sdk.ParseException;
+import com.nimbusds.oauth2.sdk.Response;
+import com.nimbusds.oauth2.sdk.http.HTTPResponse;
+import com.nimbusds.openid.connect.sdk.federation.entities.EntityStatement;
+
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.primitive.StringSupport;
+
+/**
+ * Response message to the OpenID federation explicit registration endpoint.
+ */
+public class ExplicitClientRegistrationResponse implements Response {
+
+ /** The entity statement included in the response. */
+ @Nonnull private final EntityStatement entityStatement;
+
+ /**
+ *
+ * Constructor.
+ *
+ * @param statement entity statement
+ */
+ public ExplicitClientRegistrationResponse(@Nonnull final EntityStatement statement) {
+ entityStatement = Constraint.isNotNull(statement, "Entity statement cannot be null");
+ }
+
+ /**
+ * Get the entity statement included in the response.
+ *
+ * @return entity statement
+ */
+ @Nonnull public EntityStatement getEntityStatement() {
+ return entityStatement;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean indicatesSuccess() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public HTTPResponse toHTTPResponse() {
+ final HTTPResponse httpResponse = new HTTPResponse(HTTPResponse.SC_OK);
+ httpResponse.setEntityContentType(EntityStatement.CONTENT_TYPE);
+ httpResponse.setContent(entityStatement.getSignedStatement().serialize());
+ return httpResponse;
+ }
+
+ /**
+ * Parses a federation explicit registration success response from the given HTTP response.
+ *
+ * @param httpResponse the HTTP response
+ * @return explicit registration success response
+ * @throws ParseException if HTTP response could not be parsed into registration response
+ */
+ @Nonnull
+ public static ExplicitClientRegistrationResponse parse(@Nonnull final HTTPResponse httpResponse)
+ throws ParseException {
+
+ httpResponse.ensureStatusCode(HTTPResponse.SC_OK);
+ httpResponse.ensureEntityContentType(EntityStatement.CONTENT_TYPE);
+ final String content = httpResponse.getContent();
+
+ if (StringSupport.trimOrNull(content) == null) {
+ throw new ParseException("Message body is empty");
+ }
+
+ try {
+ final EntityStatement entityStatement = EntityStatement.parse(SignedJWT.parse(httpResponse.getContent()));
+ assert entityStatement != null;
+ return new ExplicitClientRegistrationResponse(entityStatement);
+ } catch (final java.text.ParseException e) {
+ throw new ParseException(e.getMessage(), e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/BuildEntityStatement.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/AbstractBuildEntityStatementAction.java
similarity index 65%
rename from idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/BuildEntityStatement.java
rename to idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/AbstractBuildEntityStatementAction.java
index a677aed5..b810b7dd 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/BuildEntityStatement.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/AbstractBuildEntityStatementAction.java
@@ -15,9 +15,7 @@
package net.shibboleth.idp.plugin.oidc.op.oidfed.profile.impl;
import java.text.ParseException;
-import java.time.Duration;
import java.time.Instant;
-import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
@@ -27,32 +25,27 @@ import java.util.function.Function;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
+import org.opensaml.messaging.encoder.AbstractMessageEncoder;
+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.opensaml.security.credential.Credential;
import org.slf4j.Logger;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.nimbusds.jose.jwk.JWK;
-import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jwt.JWT;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.PlainJWT;
-import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
import net.shibboleth.idp.plugin.oidc.op.encoding.impl.ResponseUtil;
import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.navigate.AuthorityHintsLookupFunction;
import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.navigate.EntityStatementClaimsSetManipulationStrategyLookupFunction;
-import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.navigate.EntityStatementLifetimeLookupFunction;
import net.shibboleth.idp.profile.AbstractProfileAction;
import net.shibboleth.idp.profile.IdPEventIds;
-import net.shibboleth.oidc.profile.config.navigate.JWTSignatureSigningConfigurationLookupFunction;
-import net.shibboleth.oidc.security.CredentialConversionUtil;
-import net.shibboleth.oidc.security.jose.SignatureSigningConfiguration;
import net.shibboleth.profile.context.navigate.IssuerLookupFunction;
import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
-import net.shibboleth.shared.collection.CollectionSupport;
+import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
import net.shibboleth.shared.component.ComponentInitializationException;
import net.shibboleth.shared.logic.Constraint;
import net.shibboleth.shared.logic.FunctionSupport;
@@ -60,44 +53,29 @@ import net.shibboleth.shared.primitive.LoggerFactory;
import net.shibboleth.shared.security.IdentifierGenerationStrategy;
import net.shibboleth.shared.security.impl.SecureRandomIdentifierGenerationStrategy;
-import org.opensaml.messaging.context.navigate.ChildContextLookup;
-import org.opensaml.messaging.encoder.AbstractMessageEncoder;
-import org.opensaml.profile.action.ActionSupport;
-
/**
- * Action that creates an Entity Statement, and stores it to an {@link EntityStatementContext}.
- *
- * @event {@link EventIds#PROCEED_EVENT_ID}
- * @event {@link EventIds#INVALID_PROFILE_CTX}
- * @event {@link EventIds#IO_ERROR}
- * @event {@link IdPEventIds#INVALID_PROFILE_CONFIG}
- *
- * @since 4.3.0
+ * Abstract action used by actions that build {@link EntityStatement}s.
*/
-public class BuildEntityStatement extends AbstractProfileAction {
+public abstract class AbstractBuildEntityStatementAction extends AbstractProfileAction {
/** Class logger. */
- @Nonnull private Logger log = LoggerFactory.getLogger(BuildEntityStatement.class);
+ @Nonnull private Logger log = LoggerFactory.getLogger(AbstractBuildEntityStatementAction.class);
/** Used to log protocol messages. */
- @Nonnull private Logger protocolMessageLog =
+ @Nonnull protected Logger protocolMessageLog =
LoggerFactory.getLogger(AbstractMessageEncoder.BASE_PROTOCOL_MESSAGE_LOGGER_CATEGORY + ".OIDFED");
- /** Strategy used to obtain the response issuer value. */
+ /** Strategy used to obtain the issuer value. */
@Nonnull private Function<ProfileRequestContext,String> issuerLookupStrategy;
-
- /** Strategy used to obtain the entity statement lifetime. */
- @Nonnull private Function<ProfileRequestContext,Duration> entityStatementLifetimeLookupStrategy;
+
+ /** Strategy used to obtain the subject value. */
+ @Nonnull private Function<ProfileRequestContext,String> subjectLookupStrategy;
/** Strategy used to locate the {@link IdentifierGenerationStrategy} to use. */
@Nonnull private Function<ProfileRequestContext,IdentifierGenerationStrategy> idGeneratorLookupStrategy;
- /** Strategy used to create the subcontext to hold the statement. */
- @Nonnull private Function<ProfileRequestContext,EntityStatementContext> entityStatementContextCreationStrategy;
-
- /** Strategy used to locate the {@link SignatureSigningConfiguration}s to fetch JWK set from. */
- @Nonnull private
- Function<ProfileRequestContext,List<SignatureSigningConfiguration>> signingConfigurationsLookupStrategy;
+ /** Strategy used to locate the subcontext to hold the statement. */
+ @Nonnull private Function<ProfileRequestContext,EntityStatementContext> entityStatementContextLookupStrategy;
/** Strategy used to locate authority hints. */
@Nonnull private Function<ProfileRequestContext,List<String>> authorityHintsLookupStrategy;
@@ -114,47 +92,30 @@ public class BuildEntityStatement extends AbstractProfileAction {
@NonnullAfterInit private ObjectMapper objectMapper;
/** The generator to use. */
- @Nullable private IdentifierGenerationStrategy idGenerator;
+ @NonnullBeforeExec protected IdentifierGenerationStrategy idGenerator;
/** Entity statement context. */
- @Nullable private EntityStatementContext entityStatementCtx;
-
- /** OIDC provider metadata to publish. */
- @Nullable private OIDCProviderMetadata metadata;
+ @NonnullBeforeExec protected EntityStatementContext entityStatementCtx;
/** Constructor. */
- public BuildEntityStatement() {
- entityStatementLifetimeLookupStrategy = new EntityStatementLifetimeLookupFunction();
+ public AbstractBuildEntityStatementAction() {
issuerLookupStrategy = new IssuerLookupFunction();
+ subjectLookupStrategy = new IssuerLookupFunction();
idGeneratorLookupStrategy = FunctionSupport.constant(new SecureRandomIdentifierGenerationStrategy());
- final Function<ProfileRequestContext,EntityStatementContext> esccs =
+ final Function<ProfileRequestContext,EntityStatementContext> escls =
new ChildContextLookup<>(EntityStatementContext.class, true).compose(
new OutboundMessageContextLookup());
- assert esccs != null;
- entityStatementContextCreationStrategy = esccs;
+ assert escls != null;
+ entityStatementContextLookupStrategy = escls;
- signingConfigurationsLookupStrategy = new JWTSignatureSigningConfigurationLookupFunction();
authorityHintsLookupStrategy = new AuthorityHintsLookupFunction();
entityStatementClaimsSetManipulationStrategyLookupStrategy =
new EntityStatementClaimsSetManipulationStrategyLookupFunction();
}
- /**
- * Set the strategy used to obtain the entity statement lifetime.
- *
- * @param strategy lookup strategy
- */
- public void setEntityStatementLifetimeLookupStrategy(
- @Nonnull final Function<ProfileRequestContext,Duration> strategy) {
- ifInitializedThrowUnmodifiabledComponentException();
-
- entityStatementLifetimeLookupStrategy =
- Constraint.isNotNull(strategy, "Entity statement lifetime lookup strategy cannot be null");
- }
-
/**
* Set the strategy used to locate the {@link IdentifierGenerationStrategy} to use.
*
@@ -178,18 +139,29 @@ public class BuildEntityStatement extends AbstractProfileAction {
issuerLookupStrategy = Constraint.isNotNull(strategy, "Issuer lookup strategy cannot be null");
}
-
+
+ /**
+ * Set the strategy used to locate the subject value to use.
+ *
+ * @param strategy lookup strategy
+ */
+ public void setSubjectLookupStrategy(@Nonnull final Function<ProfileRequestContext,String> strategy) {
+ ifInitializedThrowUnmodifiabledComponentException();
+
+ subjectLookupStrategy = Constraint.isNotNull(strategy, "Subject lookup strategy cannot be null");
+ }
+
/**
- * Set the strategy used to create the {@link EntityStatementContext} to use.
+ * Set the strategy used to lookup the {@link EntityStatementContext} to use.
*
- * @param strategy creation strategy
+ * @param strategy lookup strategy
*/
- public void setEntityStatementContextCreationStrategy(
+ public void setEntityStatementContextLookupStrategy(
@Nonnull final Function<ProfileRequestContext,EntityStatementContext> strategy) {
ifInitializedThrowUnmodifiabledComponentException();
- entityStatementContextCreationStrategy =
- Constraint.isNotNull(strategy, "EntityStatementContext creation strategy cannot be null");
+ entityStatementContextLookupStrategy =
+ Constraint.isNotNull(strategy, "EntityStatementContext lookup strategy cannot be null");
}
/**
@@ -240,30 +212,13 @@ public class BuildEntityStatement extends AbstractProfileAction {
return false;
}
- entityStatementCtx = entityStatementContextCreationStrategy.apply(profileRequestContext);
+ entityStatementCtx = entityStatementContextLookupStrategy.apply(profileRequestContext);
if (entityStatementCtx == null) {
- log.error("{} Unable to create EntityStatementContext", getLogPrefix());
+ log.error("{} Unable to fetch EntityStatementContext", getLogPrefix());
ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
return false;
}
- assert entityStatementCtx != null;
- metadata = entityStatementCtx.getOPMetadata();
- if (metadata == null) {
- log.error("{} Could not resolve provider metadata", getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, EventIds.IO_ERROR);
- return false;
- }
-
- final Duration lifetime = entityStatementLifetimeLookupStrategy.apply(profileRequestContext);
- if (lifetime == null) {
- log.error("{} No lifetime supplied for entity statement", getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, IdPEventIds.INVALID_PROFILE_CONFIG);
- return false;
- }
- assert entityStatementCtx != null;
- entityStatementCtx.setLifetime(lifetime);
-
manipulationStrategy =
entityStatementClaimsSetManipulationStrategyLookupStrategy.apply(profileRequestContext);
@@ -275,39 +230,21 @@ public class BuildEntityStatement extends AbstractProfileAction {
protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
final String issuer = issuerLookupStrategy.apply(profileRequestContext);
+ final String subject = subjectLookupStrategy.apply(profileRequestContext);
final Instant now = Instant.now();
- assert entityStatementCtx != null;
- final Instant dateExp = now.plus(entityStatementCtx.getLifetime());
- assert dateExp != null;
-
- final List<SignatureSigningConfiguration> signingConfigurations =
- signingConfigurationsLookupStrategy.apply(profileRequestContext);
- if (signingConfigurations == null || signingConfigurations.isEmpty()) {
- log.error("{} Could not fetch any signature signing configurations", getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, IdPEventIds.INVALID_PROFILE_CONFIG);
- return;
- }
- final List<JWK> jwks = new ArrayList<>();
- for (final SignatureSigningConfiguration signingConfiguration : signingConfigurations) {
- for (final Credential credential : signingConfiguration.getSigningCredentials()) {
- final JWK jwk = CredentialConversionUtil.credentialToKey(credential);
- if (jwk != null) {
- jwks.add(jwk);
- log.debug("{} Included {} to the keyset", getLogPrefix(), jwk.toJSONString());
- }
- }
- }
- final JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
+ final JWTClaimsSet.Builder builder = new JWTClaimsSet.Builder()
.issuer(issuer)
- .subject(issuer)
+ .subject(subject)
.issueTime(Date.from(now))
- .expirationTime(Date.from(dateExp))
- .claim("jwks", new JWKSet(jwks).toJSONObject(true))
- .claim("authority_hints", authorityHintsLookupStrategy.apply(profileRequestContext))
- .claim("metadata", buildOpenIDProviderClaim())
- .build();
+ .claim("authority_hints", authorityHintsLookupStrategy.apply(profileRequestContext));
+ assert builder != null;
+ if (!populateClaimsSetBuilder(builder, profileRequestContext)) {
+ return;
+ }
+ final JWTClaimsSet claimsSet = builder.build();
+
assert claimsSet != null;
if (manipulationStrategy != null) {
log.debug("{} Manipulation strategy has been set, applying it to the claims set {}", getLogPrefix(),
@@ -337,11 +274,24 @@ public class BuildEntityStatement extends AbstractProfileAction {
logAndConstructEntityStatement(claimsSet);
}
- @Nonnull protected Map<String, Object> buildOpenIDProviderClaim() {
- assert metadata != null;
- return CollectionSupport.singletonMap("openid_provider", metadata.toJSONObject());
- }
+ /**
+ * Populates the claims set builder with claims specific to the action extending this abstract action. If any
+ * problem occures during population, the profile request context should be populated with an appropriate
+ * event.
+ *
+ * @param builder the claims set builder
+ * @param profileRequestContext profile request context
+ * @return true if population was successful, false otherwise
+ */
+ protected abstract boolean populateClaimsSetBuilder(@Nonnull final JWTClaimsSet.Builder builder,
+ @Nonnull final ProfileRequestContext profileRequestContext);
+ /**
+ * Logs the entity statement contents via protocol message logger and constructs a plain (i.e. non-signed) JWT out
+ * of it and includes it to the {@link EntityStatementContext#setJWT(JWT)}.
+ *
+ * @param claimsSet the claims set
+ */
protected void logAndConstructEntityStatement(@Nonnull final JWTClaimsSet claimsSet) {
log.trace("{} Building JWT from the claims set {}", getLogPrefix(), claimsSet);
final JWT jwt = new PlainJWT(claimsSet);
@@ -355,5 +305,4 @@ public class BuildEntityStatement extends AbstractProfileAction {
assert entityStatementCtx != null;
entityStatementCtx.setJWT(jwt);
}
-
-}
\ No newline at end of file
+}
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/BuildEntityConfiguration.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/BuildEntityConfiguration.java
new file mode 100644
index 00000000..21a4dd2d
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/BuildEntityConfiguration.java
@@ -0,0 +1,148 @@
+/*
+ * 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.time.Duration;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.profile.action.EventIds;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.security.credential.Credential;
+import org.slf4j.Logger;
+
+import com.nimbusds.jose.jwk.JWK;
+import com.nimbusds.jose.jwk.JWKSet;
+import com.nimbusds.jwt.JWTClaimsSet;
+import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
+
+import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.navigate.EntityStatementLifetimeLookupFunction;
+import net.shibboleth.idp.profile.IdPEventIds;
+import net.shibboleth.oidc.profile.config.navigate.JWTSignatureSigningConfigurationLookupFunction;
+import net.shibboleth.oidc.security.CredentialConversionUtil;
+import net.shibboleth.oidc.security.jose.SignatureSigningConfiguration;
+import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
+import net.shibboleth.shared.collection.CollectionSupport;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.primitive.LoggerFactory;
+
+import org.opensaml.profile.action.ActionSupport;
+
+/**
+ * Action that creates an Entity Statement, and stores it to an {@link EntityStatementContext}.
+ *
+ * @event {@link EventIds#PROCEED_EVENT_ID}
+ * @event {@link EventIds#INVALID_PROFILE_CTX}
+ * @event {@link EventIds#IO_ERROR}
+ * @event {@link IdPEventIds#INVALID_PROFILE_CONFIG}
+ *
+ * @since 4.3.0
+ */
+public class BuildEntityConfiguration extends AbstractBuildEntityStatementAction {
+
+ /** Class logger. */
+ @Nonnull private Logger log = LoggerFactory.getLogger(BuildEntityConfiguration.class);
+
+ /** Strategy used to locate the {@link SignatureSigningConfiguration}s to fetch JWK set from. */
+ @Nonnull private
+ Function<ProfileRequestContext,List<SignatureSigningConfiguration>> signingConfigurationsLookupStrategy;
+
+ /** Strategy used to obtain the entity statement lifetime. */
+ @Nonnull private Function<ProfileRequestContext,Duration> entityConfigurationLifetimeLookupStrategy;
+
+ /** OIDC provider metadata to publish. */
+ @NonnullBeforeExec private OIDCProviderMetadata metadata;
+
+ /** Constructor. */
+ public BuildEntityConfiguration() {
+ signingConfigurationsLookupStrategy = new JWTSignatureSigningConfigurationLookupFunction();
+ entityConfigurationLifetimeLookupStrategy = new EntityStatementLifetimeLookupFunction();
+ }
+
+ /**
+ * Set the strategy used to obtain the entity configuration lifetime.
+ *
+ * @param strategy lookup strategy
+ */
+ public void setEntityConfigurationLifetimeLookupStrategy(
+ @Nonnull final Function<ProfileRequestContext,Duration> strategy) {
+ ifInitializedThrowUnmodifiabledComponentException();
+
+ entityConfigurationLifetimeLookupStrategy =
+ Constraint.isNotNull(strategy, "Entity configuration lifetime lookup strategy cannot be null");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
+ if (!super.doPreExecute(profileRequestContext)) {
+ return false;
+ }
+
+ metadata = entityStatementCtx.getOPMetadata();
+ if (metadata == null) {
+ log.error("{} Could not resolve provider metadata", getLogPrefix());
+ ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
+ return false;
+ }
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected boolean populateClaimsSetBuilder(@Nonnull final JWTClaimsSet.Builder builder,
+ @Nonnull final ProfileRequestContext profileRequestContext) {
+ final List<SignatureSigningConfiguration> signingConfigurations =
+ signingConfigurationsLookupStrategy.apply(profileRequestContext);
+ if (signingConfigurations == null || signingConfigurations.isEmpty()) {
+ log.error("{} Could not fetch any signature signing configurations", getLogPrefix());
+ ActionSupport.buildEvent(profileRequestContext, IdPEventIds.INVALID_PROFILE_CONFIG);
+ return false;
+ }
+
+ final List<JWK> jwks = new ArrayList<>();
+ for (final SignatureSigningConfiguration signingConfiguration : signingConfigurations) {
+ for (final Credential credential : signingConfiguration.getSigningCredentials()) {
+ final JWK jwk = CredentialConversionUtil.credentialToKey(credential);
+ if (jwk != null) {
+ jwks.add(jwk);
+ log.debug("{} Included {} to the keyset", getLogPrefix(), jwk.toJSONString());
+ }
+ }
+ }
+
+ final Duration lifetime = entityConfigurationLifetimeLookupStrategy.apply(profileRequestContext);
+ if (lifetime == null) {
+ log.error("{} No lifetime supplied for entity statement", getLogPrefix());
+ ActionSupport.buildEvent(profileRequestContext, IdPEventIds.INVALID_PROFILE_CONFIG);
+ return false;
+ }
+ final Instant now = Instant.now();
+ final Instant dateExp = now.plus(lifetime);
+ assert dateExp != null;
+
+ builder.expirationTime(Date.from(dateExp));
+ builder.claim("jwks", new JWKSet(jwks).toJSONObject(true));
+ builder.claim("metadata", CollectionSupport.singletonMap("openid_provider", metadata.toJSONObject()));
+ return true;
+ }
+
+}
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/BuildExplicitRegistrationResponse.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/BuildExplicitRegistrationResponse.java
new file mode 100644
index 00000000..368ea369
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/BuildExplicitRegistrationResponse.java
@@ -0,0 +1,172 @@
+/*
+ * 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.time.Instant;
+import java.util.Date;
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+
+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.InboundMessageContextLookup;
+import org.opensaml.profile.context.navigate.OutboundMessageContextLookup;
+import org.slf4j.Logger;
+
+import com.nimbusds.jose.jwk.JWKSet;
+import com.nimbusds.jwt.JWTClaimsSet;
+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.oidfed.profile.navigate.ExplicitClientRegistrationRequestJWKSetLookupFunction;
+import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
+import net.shibboleth.shared.collection.CollectionSupport;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.primitive.LoggerFactory;
+
+/**
+ * An action that uses the information from {@link OIDCClientRegistrationResponseContext} attached to the message
+ * context for creating a new JWT to be used for creating a response to OpenID Federation Explicit Registration.
+ */
+public class BuildExplicitRegistrationResponse extends AbstractBuildEntityStatementAction {
+
+ /** Class logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(BuildExplicitRegistrationResponse.class);
+
+ /**
+ * Strategy used to locate the {@link OIDCClientRegistrationResponseContext}.
+ */
+ @Nonnull private Function<ProfileRequestContext,OIDCClientRegistrationResponseContext> oidcResponseContextLookupStrategy;
+
+ /** Strategy used to locate the JWK set to be included in the response entity statement. */
+ @Nonnull private Function<ProfileRequestContext,JWKSet> jwkSetLookupStrategy;
+
+ /** Strategy used to lookup the trust chain context. */
+ @Nonnull private Function<ProfileRequestContext, RelyingPartyTrustChainContext> trustChainContextLookupStrategy;
+
+ /** The {@link OIDCClientRegistrationResponseContext} to operate on. */
+ @NonnullBeforeExec private OIDCClientRegistrationResponseContext oidcResponseContext;
+
+ /** Trust chain context to operate on. */
+ @NonnullBeforeExec private RelyingPartyTrustChainContext trustChainContext;
+
+ /** Constructor. */
+ public BuildExplicitRegistrationResponse() {
+ final Function<ProfileRequestContext, OIDCClientRegistrationResponseContext> ocrrls =
+ new ChildContextLookup<>(OIDCClientRegistrationResponseContext.class)
+ .compose(new OutboundMessageContextLookup());
+ assert ocrrls != null;
+ oidcResponseContextLookupStrategy = ocrrls;
+ jwkSetLookupStrategy = new ExplicitClientRegistrationRequestJWKSetLookupFunction();
+ final Function<ProfileRequestContext, RelyingPartyTrustChainContext> tcls =
+ new ChildContextLookup<>(RelyingPartyTrustChainContext.class).compose(
+ new InboundMessageContextLookup());
+ assert tcls != null;
+ trustChainContextLookupStrategy = tcls;
+ }
+
+ /**
+ * Set the strategy used to locate the {@link OIDCClientRegistrationResponseContext}.
+ *
+ * @param strategy lookup strategy
+ */
+ public void setOidcResponseContextLookupStrategy(
+ @Nonnull final Function<ProfileRequestContext,OIDCClientRegistrationResponseContext> strategy) {
+ ifInitializedThrowUnmodifiabledComponentException();
+
+ oidcResponseContextLookupStrategy = Constraint.isNotNull(strategy,
+ "OIDCClientRegistrationResponseContext lookup strategy cannot be null");
+ }
+
+ /**
+ * Set the strategy used to locate the JWK set to be included in the response entity statement.
+ *
+ * @param strategy lookup strategy
+ */
+ public void setJwkSetLookupStrategy(@Nonnull final Function<ProfileRequestContext,JWKSet> strategy) {
+ checkSetterPreconditions();
+ jwkSetLookupStrategy = Constraint.isNotNull(strategy, "JWK set loookup strategy cannot be null");
+ }
+
+ /**
+ * Set the strategy used to lookup the trust chain context.
+ *
+ * @param strategy lookup strategy
+ */
+ public void setTrustChainContextLookupStrategy(
+ @Nonnull final Function<ProfileRequestContext, RelyingPartyTrustChainContext> strategy) {
+ checkSetterPreconditions();
+ trustChainContextLookupStrategy =
+ Constraint.isNotNull(strategy, "TrustChainContextLookupStrategy cannot be null");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
+ if (!super.doPreExecute(profileRequestContext)) {
+ return false;
+ }
+
+ oidcResponseContext = oidcResponseContextLookupStrategy.apply(profileRequestContext);
+ if (oidcResponseContext == null) {
+ log.debug("{} No OIDCClientRegistrationResponseContext associated with this profile request",
+ getLogPrefix());
+ ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
+ return false;
+ }
+
+ trustChainContext = trustChainContextLookupStrategy.apply(profileRequestContext);
+ if (trustChainContext == null || trustChainContext.getPolicyCompliantTrustChains() == null) {
+ log.error("{} Unable to locate policy-compliant trust chains", getLogPrefix());
+ ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MSG_CTX);
+ return false;
+ }
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected boolean populateClaimsSetBuilder(@Nonnull final JWTClaimsSet.Builder builder,
+ @Nonnull final ProfileRequestContext profileRequestContext) {
+ final OIDCClientInformation clientInformation = oidcResponseContext.getClientInformation();
+ if (clientInformation == null) {
+ log.debug("{} No client information set in the OIDC response context", getLogPrefix());
+ ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
+ return false;
+ }
+ builder.claim("metadata",
+ CollectionSupport.singletonMap("openid_relying_party", clientInformation.toJSONObject()));
+ final JWKSet jwkSet = jwkSetLookupStrategy.apply(profileRequestContext);
+ if (jwkSet == null) {
+ log.error("{} Coud not resolve JWK set to be included in the response statement", getLogPrefix());
+ ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MSG_CTX);
+ return false;
+ }
+ builder.claim("jwks", jwkSet.toJSONObject(true));
+ final Instant expirationTime = trustChainContext.getSelectedMetadataExpiration();
+ if (expirationTime == null) {
+ log.error("{} Coud not resolve expiration time from the selected trust chain context", getLogPrefix());
+ ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MSG_CTX);
+ return false;
+ }
+ builder.expirationTime(Date.from(expirationTime));
+ return true;
+ }
+
+}
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/FormExplicitRegistrationResponse.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/FormExplicitRegistrationResponse.java
new file mode 100644
index 00000000..425971d1
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/FormExplicitRegistrationResponse.java
@@ -0,0 +1,118 @@
+/*
+ * 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.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.jwt.SignedJWT;
+import com.nimbusds.oauth2.sdk.ParseException;
+import com.nimbusds.openid.connect.sdk.federation.entities.EntityStatement;
+
+import net.shibboleth.idp.plugin.oidc.op.oidfed.messaging.impl.ExplicitClientRegistrationResponse;
+import net.shibboleth.idp.profile.AbstractProfileAction;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.primitive.LoggerFactory;
+
+/**
+ * This action builds a response for the OpenID federation explicit registration request. The response contains an
+ * {@link EntityStatement}.
+ *
+ * @since 4.3.0
+ */
+public class FormExplicitRegistrationResponse extends AbstractProfileAction {
+
+ /** Class logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(FormExplicitRegistrationResponse.class);
+
+ /** Strategy used to locate the subcontext to hold the statement. */
+ @Nonnull private Function<ProfileRequestContext,EntityStatementContext> entityStatementContextLookupStrategy;
+
+ /** JWT used to build entity statement. */
+ @Nullable private SignedJWT jwt;
+
+ /**
+ * Constructor.
+ */
+ public FormExplicitRegistrationResponse() {
+ final Function<ProfileRequestContext,EntityStatementContext> escls =
+ new ChildContextLookup<>(EntityStatementContext.class).compose(
+ new OutboundMessageContextLookup());
+ assert escls != null;
+ entityStatementContextLookupStrategy = escls;
+ }
+
+ /**
+ * Set the strategy used to locate the subcontext to hold the statement
+ *
+ * @param strategy What to set.
+ */
+ public void setEntityStatementContextLookupStrategy(
+ @Nonnull final Function<ProfileRequestContext,EntityStatementContext> strategy) {
+ checkSetterPreconditions();
+ entityStatementContextLookupStrategy = Constraint.isNotNull(strategy, "Lookup strategy cannot be null!");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
+
+ if (!super.doPreExecute(profileRequestContext)) {
+ return false;
+ }
+ final EntityStatementContext entityStatementContext =
+ entityStatementContextLookupStrategy.apply(profileRequestContext);
+ if (entityStatementContext == null) {
+ log.error("{} Could not resolve entity statement context", getLogPrefix());
+ ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
+ return false;
+ }
+ if (entityStatementContext.getJWT() instanceof SignedJWT signedJwt) {
+ jwt = signedJwt;
+ } else {
+ log.error("{} No signed JWT found from the entity statement context", getLogPrefix());
+ ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
+ return false;
+ }
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
+ final EntityStatement entityStatement;
+ try {
+ entityStatement = EntityStatement.parse(jwt);
+ } catch (ParseException e) {
+ log.error("{} Could not parse entity statement from JWT", getLogPrefix(), e);
+ ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
+ return;
+ }
+ assert entityStatement != null;
+ final ExplicitClientRegistrationResponse response = new ExplicitClientRegistrationResponse(entityStatement);
+ log.debug("{} Response message set to the outbound message context", getLogPrefix());
+ profileRequestContext.ensureOutboundMessageContext().setMessage(response);
+ }
+}
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultSelectedTrustChainMetadataLookupStrategy.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultSelectedTrustChainMetadataLookupStrategy.java
new file mode 100644
index 00000000..6780b041
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultSelectedTrustChainMetadataLookupStrategy.java
@@ -0,0 +1,76 @@
+/*
+ * 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.navigate;
+
+import java.util.Optional;
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.profile.context.navigate.InboundMessageContextLookup;
+
+import com.nimbusds.openid.connect.sdk.rp.OIDCClientMetadata;
+
+import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.impl.RelyingPartyTrustChainContext;
+import net.shibboleth.shared.annotation.ParameterName;
+import net.shibboleth.shared.logic.Constraint;
+
+/**
+ * Default strategy for looking up the metadata the selected trust chain. The selected trust chain is fetched via
+ * {@link RelyingPartyTrustChainContext#getSelectedTrustChain()}.
+ */
+public class DefaultSelectedTrustChainMetadataLookupStrategy
+ implements Function<ProfileRequestContext,OIDCClientMetadata> {
+
+ /** Strategy used to locate the trust chain context. */
+ @Nonnull private Function<ProfileRequestContext, RelyingPartyTrustChainContext> trustChainContextLookupStrategy;
+
+ /**
+ * Constructor.
+ */
+ public DefaultSelectedTrustChainMetadataLookupStrategy() {
+ final Function<ProfileRequestContext, RelyingPartyTrustChainContext> tcls =
+ new ChildContextLookup<>(RelyingPartyTrustChainContext.class, true).compose(
+ new InboundMessageContextLookup());
+ assert tcls != null;
+ trustChainContextLookupStrategy = tcls;
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param trustChainContextStrategy strategy used to locate the trust chain context
+ * @param trustChainIDsStrategy strategy used to get entity IDs from a trust chain
+ */
+ public DefaultSelectedTrustChainMetadataLookupStrategy(
+ @Nonnull @ParameterName(name = "trustChainContextLookupStrategy")
+ final Function<ProfileRequestContext, RelyingPartyTrustChainContext> trustChainContextStrategy) {
+ trustChainContextLookupStrategy =
+ Constraint.isNotNull(trustChainContextStrategy, "TrustChainContextLookupStrategy cannot be null");
+ }
+
+ /** {@inheritDoc} */
+ @Override @Nullable
+ public OIDCClientMetadata apply(@Nullable final ProfileRequestContext input) {
+ return Optional.ofNullable(trustChainContextLookupStrategy.apply(input))
+ .map(trustChainContext -> trustChainContext.getSelectedTrustChain())
+ .map(pair -> pair.getSecond())
+ .map(clientInfo -> clientInfo.getOIDCMetadata())
+ .orElse(null);
+ }
+}
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/ExplicitClientRegistrationRequestClientIDLookupFunction.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/ExplicitClientRegistrationRequestClientIDLookupFunction.java
new file mode 100644
index 00000000..ffb28564
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/ExplicitClientRegistrationRequestClientIDLookupFunction.java
@@ -0,0 +1,68 @@
+/*
+ * 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.navigate;
+
+import java.util.List;
+import java.util.Optional;
+
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.ThreadSafe;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.messaging.context.navigate.ContextDataLookupFunction;
+
+import com.nimbusds.oauth2.sdk.id.ClientID;
+import com.nimbusds.openid.connect.sdk.federation.entities.EntityStatement;
+
+import net.shibboleth.idp.plugin.oidc.op.oidfed.messaging.impl.ExplicitClientRegistrationRequest;
+
+/**
+ * A function that returns client from the entity configuration in the explicit registration request.
+ *
+ * @since 4.3.0
+ */
+ at ThreadSafe
+public class ExplicitClientRegistrationRequestClientIDLookupFunction
+ implements ContextDataLookupFunction<MessageContext, ClientID> {
+
+ /** {@inheritDoc} */
+ @Nullable
+ public ClientID apply(@Nullable final MessageContext input) {
+ return Optional.ofNullable(input)
+ .map(messageContext -> messageContext.getMessage())
+ .filter(ExplicitClientRegistrationRequest.class::isInstance)
+ .map(ExplicitClientRegistrationRequest.class::cast)
+ .map(request -> getClientID(request))
+ .orElse(null);
+ }
+
+ /**
+ * Get the client ID from the explicit registration request.
+ *
+ * @param request the explicit registration request
+ * @return the client ID, or null if it cannot be parsed
+ */
+ @Nullable protected ClientID getClientID(@Nullable final ExplicitClientRegistrationRequest request) {
+ if (request == null) {
+ return null;
+ }
+ final EntityStatement entityConfiguration = request.getEntityConfiguration();
+ if (entityConfiguration != null) {
+ return new ClientID(entityConfiguration.getEntityID());
+ }
+ final List<EntityStatement> trustChain = request.getTrustChain();
+ return trustChain != null && !trustChain.isEmpty() ? new ClientID(trustChain.get(0).getEntityID()) : null;
+ }
+}
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/ExplicitClientRegistrationRequestJWKSetLookupFunction.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/ExplicitClientRegistrationRequestJWKSetLookupFunction.java
new file mode 100644
index 00000000..d574b93e
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/ExplicitClientRegistrationRequestJWKSetLookupFunction.java
@@ -0,0 +1,69 @@
+/*
+ * 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.navigate;
+
+import java.util.List;
+import java.util.Optional;
+
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.ThreadSafe;
+
+import org.opensaml.messaging.context.navigate.ContextDataLookupFunction;
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import com.nimbusds.jose.jwk.JWKSet;
+import com.nimbusds.openid.connect.sdk.federation.entities.EntityStatement;
+
+import net.shibboleth.idp.plugin.oidc.op.oidfed.messaging.impl.ExplicitClientRegistrationRequest;
+
+/**
+ * A function that returns {@link JWKSet} from the entity configuration in the explicit registration request.
+ *
+ * @since 4.3.0
+ */
+ at ThreadSafe
+public class ExplicitClientRegistrationRequestJWKSetLookupFunction
+ implements ContextDataLookupFunction<ProfileRequestContext, JWKSet> {
+
+ /** {@inheritDoc} */
+ @Nullable
+ public JWKSet apply(@Nullable final ProfileRequestContext input) {
+ return Optional.ofNullable(input)
+ .map(profileRequesContext -> profileRequesContext.getInboundMessageContext())
+ .map(messageContext -> messageContext.getMessage())
+ .filter(ExplicitClientRegistrationRequest.class::isInstance)
+ .map(ExplicitClientRegistrationRequest.class::cast)
+ .map(request -> getJWKSet(request))
+ .orElse(null);
+ }
+
+ /**
+ * Get the JSON Web Key Set from the explicit registration request.
+ *
+ * @param request explicit registration request
+ * @return the JWKSet, or null if it cannot be parsed
+ */
+ @Nullable protected JWKSet getJWKSet(@Nullable final ExplicitClientRegistrationRequest request) {
+ if (request == null) {
+ return null;
+ }
+ final EntityStatement entityConfiguration = request.getEntityConfiguration();
+ if (entityConfiguration != null) {
+ return entityConfiguration.getClaimsSet().getJWKSet();
+ }
+ final List<EntityStatement> trustChain = request.getTrustChain();
+ return trustChain != null && !trustChain.isEmpty() ? trustChain.get(0).getClaimsSet().getJWKSet() : null;
+ }
+}
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/LocalMetadataPolicyLookupFunction.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/LocalMetadataPolicyLookupFunction.java
index 54acc5dd..41cc47d8 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/LocalMetadataPolicyLookupFunction.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/LocalMetadataPolicyLookupFunction.java
@@ -23,12 +23,12 @@ import org.opensaml.profile.context.ProfileRequestContext;
import net.shibboleth.profile.config.ProfileConfiguration;
import net.shibboleth.profile.context.RelyingPartyContext;
import net.shibboleth.profile.context.navigate.AbstractRelyingPartyLookupFunction;
-import net.shibboleth.idp.plugin.oidc.op.oidfed.config.OIDFederationAutomaticRegistrationConfiguration;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.config.OIDFederationRegistrationProfileConfiguration;
import net.shibboleth.oidc.metadata.policy.MetadataPolicy;
/**
* A function that obtains
- * {@link OIDFederationAutomaticRegistrationConfiguration#getLocalMetadataPolicy(ProfileRequestContext)}.
+ * {@link OIDFederationRegistrationProfileConfiguration#getLocalMetadataPolicy(ProfileRequestContext)}.
*
* <p>If a specific setting is unavailable, a null value is returned.</p>
*/
@@ -40,8 +40,8 @@ public class LocalMetadataPolicyLookupFunction extends AbstractRelyingPartyLooku
final RelyingPartyContext rpc = getRelyingPartyContextLookupStrategy().apply(input);
if (rpc != null) {
final ProfileConfiguration pc = rpc.getProfileConfig();
- if (pc instanceof OIDFederationAutomaticRegistrationConfiguration ofarc) {
- return ofarc.getLocalMetadataPolicy(input);
+ if (pc instanceof OIDFederationRegistrationProfileConfiguration ofrpc) {
+ return ofrpc.getLocalMetadataPolicy(input);
}
}
return null;
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/MandatoryTrustMarksLookupFunction.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/MandatoryTrustMarksLookupFunction.java
index 3a4b8f39..78542677 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/MandatoryTrustMarksLookupFunction.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/MandatoryTrustMarksLookupFunction.java
@@ -23,11 +23,11 @@ import org.opensaml.profile.context.ProfileRequestContext;
import net.shibboleth.profile.config.ProfileConfiguration;
import net.shibboleth.profile.context.RelyingPartyContext;
import net.shibboleth.profile.context.navigate.AbstractRelyingPartyLookupFunction;
-import net.shibboleth.idp.plugin.oidc.op.oidfed.config.OIDFederationAutomaticRegistrationConfiguration;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.config.OIDFederationRegistrationProfileConfiguration;
/**
* A function that obtains
- * {@link OIDFederationAutomaticRegistrationConfiguration#getMandatoryTrustMarks(ProfileRequestContext)}.
+ * {@link OIDFederationRegistrationProfileConfiguration#getMandatoryTrustMarks(ProfileRequestContext)}.
*
* <p>If a specific setting is unavailable, a null value is returned.</p>
*/
@@ -39,8 +39,8 @@ public class MandatoryTrustMarksLookupFunction extends AbstractRelyingPartyLooku
final RelyingPartyContext rpc = getRelyingPartyContextLookupStrategy().apply(input);
if (rpc != null) {
final ProfileConfiguration pc = rpc.getProfileConfig();
- if (pc instanceof OIDFederationAutomaticRegistrationConfiguration ofarc) {
- return ofarc.getMandatoryTrustMarks(input);
+ if (pc instanceof OIDFederationRegistrationProfileConfiguration ofrpc) {
+ return ofrpc.getMandatoryTrustMarks(input);
}
}
return null;
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/MaximumTrustMarkLifetimeLookupFunction.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/MaximumTrustMarkLifetimeLookupFunction.java
index c39ed5d7..99986547 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/MaximumTrustMarkLifetimeLookupFunction.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/MaximumTrustMarkLifetimeLookupFunction.java
@@ -23,11 +23,11 @@ import org.opensaml.profile.context.ProfileRequestContext;
import net.shibboleth.profile.config.ProfileConfiguration;
import net.shibboleth.profile.context.RelyingPartyContext;
import net.shibboleth.profile.context.navigate.AbstractRelyingPartyLookupFunction;
-import net.shibboleth.idp.plugin.oidc.op.oidfed.config.OIDFederationAutomaticRegistrationConfiguration;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.config.OIDFederationRegistrationProfileConfiguration;
/**
* A function that returns
- * {@link OIDFederationAutomaticRegistrationConfiguration#getMaximumTrustMarkLifetime(ProfileRequestContext)} if such a
+ * {@link OIDFederationRegistrationProfileConfiguration#getMaximumTrustMarkLifetime(ProfileRequestContext)} if such a
* profile is available from a {@link RelyingPartyContext} obtained via a lookup function, by default a child of the
* {@link ProfileRequestContext}.
*
@@ -41,8 +41,8 @@ public class MaximumTrustMarkLifetimeLookupFunction extends AbstractRelyingParty
final RelyingPartyContext rpc = getRelyingPartyContextLookupStrategy().apply(input);
if (rpc != null) {
final ProfileConfiguration pc = rpc.getProfileConfig();
- if (pc instanceof OIDFederationAutomaticRegistrationConfiguration ofarc) {
- return ofarc.getMaximumTrustMarkLifetime(input);
+ if (pc instanceof OIDFederationRegistrationProfileConfiguration ofrpc) {
+ return ofrpc.getMaximumTrustMarkLifetime(input);
}
}
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/AbstractOIDCClientRegistrationAction.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/AbstractOIDCClientRegistrationAction.java
new file mode 100644
index 00000000..8e53926f
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/AbstractOIDCClientRegistrationAction.java
@@ -0,0 +1,98 @@
+/*
+ * 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.impl;
+
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+
+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 net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCClientRegistrationResponseContext;
+import net.shibboleth.idp.profile.AbstractProfileAction;
+import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.primitive.LoggerFactory;
+
+/**
+ * Abstract action for dynamic client registration actions dealing with {@link OIDCClientRegistrationResponseContext}.
+ */
+public abstract class AbstractOIDCClientRegistrationAction extends AbstractProfileAction {
+
+ /** Class logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(AbstractOIDCClientRegistrationAction.class);
+
+ /** The strategy used to locate the {@link OIDCClientRegistrationResponseContext}. */
+ @Nonnull
+ private Function<ProfileRequestContext,OIDCClientRegistrationResponseContext> oidcResponseContextLookupStrategy;
+
+ /** The {@link OIDCClientRegistrationResponseContext} to operate on. */
+ @NonnullBeforeExec private OIDCClientRegistrationResponseContext oidcResponseContext;
+
+ /** Constructor. */
+ public AbstractOIDCClientRegistrationAction() {
+ final Function<ProfileRequestContext, OIDCClientRegistrationResponseContext> ocrrls =
+ new ChildContextLookup<>(OIDCClientRegistrationResponseContext.class)
+ .compose(new OutboundMessageContextLookup());
+ assert ocrrls != null;
+ oidcResponseContextLookupStrategy = ocrrls;
+ }
+
+ /**
+ * Set the strategy used to locate the {@link OIDCClientRegistrationResponseContext}.
+ *
+ * @param strategy lookup strategy
+ */
+ public void setOidcResponseContextLookupStrategy(
+ @Nonnull final Function<ProfileRequestContext,OIDCClientRegistrationResponseContext> strategy) {
+ checkSetterPreconditions();
+ oidcResponseContextLookupStrategy = Constraint.isNotNull(strategy,
+ "The output OIDCClientRegistrationResponseContext lookup strategy cannot be null");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
+
+ if (!super.doPreExecute(profileRequestContext)) {
+ return false;
+ }
+
+ oidcResponseContext = oidcResponseContextLookupStrategy.apply(profileRequestContext);
+ if (oidcResponseContext == null) {
+ log.debug("{} No OIDCClientRegistrationResponseContext associated with this profile request",
+ getLogPrefix());
+ ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MSG_CTX);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Get the {@link OIDCClientRegistrationResponseContext}. Cannot be null after
+ * {@link this#doPreExecute(ProfileRequestContext)} has returned true.
+ *
+ * @return registration context
+ */
+ @NonnullBeforeExec protected OIDCClientRegistrationResponseContext getRegistrationContext() {
+ return oidcResponseContext;
+ }
+}
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/BuildClientInformation.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/BuildClientInformation.java
index bf5a948a..8bdfad3f 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/BuildClientInformation.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/BuildClientInformation.java
@@ -16,12 +16,9 @@ package net.shibboleth.idp.plugin.oidc.op.profile.impl;
import java.time.Instant;
import java.util.Date;
-import java.util.function.Function;
import javax.annotation.Nonnull;
-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;
@@ -32,12 +29,9 @@ import com.nimbusds.oauth2.sdk.auth.Secret;
import com.nimbusds.oauth2.sdk.client.ClientInformationResponse;
import com.nimbusds.oauth2.sdk.id.ClientID;
import com.nimbusds.openid.connect.sdk.rp.OIDCClientInformation;
-import com.nimbusds.openid.connect.sdk.rp.OIDCClientInformationResponse;
import com.nimbusds.openid.connect.sdk.rp.OIDCClientMetadata;
import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCClientRegistrationResponseContext;
-import net.shibboleth.idp.profile.AbstractProfileAction;
-import net.shibboleth.shared.logic.Constraint;
import net.shibboleth.shared.primitive.LoggerFactory;
import net.shibboleth.shared.primitive.StringSupport;
@@ -45,70 +39,15 @@ import net.shibboleth.shared.primitive.StringSupport;
* An action that uses the information from {@link OIDCClientRegistrationResponseContext} attached to the message
* context for creating a new {@link ClientInformationResponse}. It will be set as the outbound message.
*/
-public class BuildClientInformation extends AbstractProfileAction {
+public class BuildClientInformation extends AbstractOIDCClientRegistrationAction {
/** Class logger. */
@Nonnull private final Logger log = LoggerFactory.getLogger(BuildClientInformation.class);
- /**
- * Strategy used to locate the {@link OIDCClientRegistrationResponseContext} associated with a given
- * {@link MessageContext}.
- */
- @Nonnull private Function<MessageContext,OIDCClientRegistrationResponseContext> oidcResponseContextLookupStrategy;
-
- /** The {@link MessageContext} to operate on. */
- private MessageContext messageContext;
-
- /** The {@link OIDCClientRegistrationResponseContext} to operate on. */
- private OIDCClientRegistrationResponseContext oidcResponseContext;
-
- /** Constructor. */
- public BuildClientInformation() {
- oidcResponseContextLookupStrategy = new ChildContextLookup<>(OIDCClientRegistrationResponseContext.class);
- }
-
- /**
- * 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<MessageContext,OIDCClientRegistrationResponseContext> strategy) {
- ifInitializedThrowUnmodifiabledComponentException();
-
- oidcResponseContextLookupStrategy = Constraint.isNotNull(strategy,
- "OIDCClientRegistrationResponseContext lookup strategy cannot be null");
- }
-
- /** {@inheritDoc} */
- @Override
- protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
- if (!super.doPreExecute(profileRequestContext)) {
- return false;
- }
-
- messageContext = profileRequestContext.getOutboundMessageContext();
- if (messageContext == null) {
- log.error("{} No message context found", getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
- return false;
- }
-
- oidcResponseContext = oidcResponseContextLookupStrategy.apply(messageContext);
- if (oidcResponseContext == null) {
- log.error("{} No OIDC response context found", getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MSG_CTX);
- return false;
- }
-
- return true;
- }
-
/** {@inheritDoc} */
@Override
protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
+ final OIDCClientRegistrationResponseContext oidcResponseContext = getRegistrationContext();
final String id = oidcResponseContext.getClientId();
if (StringSupport.trimOrNull(id) == null) {
@@ -150,9 +89,7 @@ public class BuildClientInformation extends AbstractProfileAction {
final OIDCClientInformation clientInformation = new OIDCClientInformation(clientId, new Date(),
metadata, clientSecret);
- final OIDCClientInformationResponse response = new OIDCClientInformationResponse(clientInformation, true);
- messageContext.setMessage(response);
- log.info("{} Client information successfully added to the outbound context", getLogPrefix());
-
+ oidcResponseContext.setClientInformation(clientInformation);
+ log.info("{} Client information successfully populated to the response context", getLogPrefix());
}
}
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/CheckRedirectURIs.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/CheckRedirectURIs.java
index 54fa8ca1..a5ff735a 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/CheckRedirectURIs.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/CheckRedirectURIs.java
@@ -21,6 +21,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
+import java.util.function.Function;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -44,11 +45,12 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.nimbusds.oauth2.sdk.GrantType;
import com.nimbusds.openid.connect.sdk.rp.ApplicationType;
import com.nimbusds.openid.connect.sdk.rp.OIDCClientMetadata;
-import com.nimbusds.openid.connect.sdk.rp.OIDCClientRegistrationRequest;
+import net.shibboleth.idp.plugin.oidc.op.messaging.context.navigate.DefaultRequestedMetadataLookupFunction;
import net.shibboleth.idp.profile.AbstractProfileAction;
import net.shibboleth.oidc.profile.core.OidcEventIds;
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;
@@ -73,9 +75,6 @@ public class CheckRedirectURIs extends AbstractProfileAction {
@Nonnull
private final Logger log = LoggerFactory.getLogger(CheckRedirectURIs.class);
- /** The OIDCClientRegistrationRequest to check redirect URIs from. */
- @Nullable private OIDCClientRegistrationRequest request;
-
/** The {@link HttpClient} to use. */
@NonnullAfterInit private HttpClient httpClient;
@@ -85,9 +84,16 @@ public class CheckRedirectURIs extends AbstractProfileAction {
/** JSON object mapper. */
@NonnullAfterInit private ObjectMapper objectMapper;
+ /** Lookup strategy for requested metadata */
+ @Nonnull private Function<ProfileRequestContext, OIDCClientMetadata> requestMetadataLookupStrategy;
+
+ /** Requested metadata to operate on. */
+ @NonnullBeforeExec private OIDCClientMetadata metadata;
+
/** Constructor. */
public CheckRedirectURIs() {
super();
+ requestMetadataLookupStrategy = new DefaultRequestedMetadataLookupFunction();
}
/**
@@ -125,6 +131,18 @@ public class CheckRedirectURIs extends AbstractProfileAction {
objectMapper = Constraint.isNotNull(mapper, "Object mapper cannot be null");
}
+ /**
+ * Set the lookup strategy for requested metadata.
+ *
+ * @param strategy lookup strategy
+ */
+ public void setRequestMetadataLookupStrategy(
+ @Nonnull final Function<ProfileRequestContext, OIDCClientMetadata> strategy) {
+ checkSetterPreconditions();
+
+ requestMetadataLookupStrategy =
+ Constraint.isNotNull(strategy, "Request metadata lookup strategy cannot be null");
+ }
/** {@inheritDoc} */
public void doInitialize() throws ComponentInitializationException {
super.doInitialize();
@@ -150,13 +168,12 @@ public class CheckRedirectURIs extends AbstractProfileAction {
ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
return false;
}
- final Object message = profileRequestContext.ensureInboundMessageContext().getMessage();
- if (message == null || !(message instanceof OIDCClientRegistrationRequest)) {
- log.debug("{} No inbound message associated with this profile request", getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MSG_CTX);
- return false;
+ metadata = requestMetadataLookupStrategy.apply(profileRequestContext);
+ if (metadata == null) {
+ log.warn("{} No client metadata found in the request", getLogPrefix());
+ ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MESSAGE);
+ return false;
}
- request = (OIDCClientRegistrationRequest) message;
return true;
}
@@ -165,13 +182,6 @@ public class CheckRedirectURIs extends AbstractProfileAction {
/** {@inheritDoc} */
@Override
protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
- assert request != null;
- final OIDCClientMetadata metadata = request.getOIDCClientMetadata();
- if (metadata == null) {
- log.warn("{} No client metadata found in the request", getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MESSAGE);
- return;
- }
final Set<URI> redirectURIs = metadata.getRedirectionURIs();
if (redirectURIs == null || redirectURIs.isEmpty()) {
log.warn("{} No redirection URIs found in the request", getLogPrefix());
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/FormOutboundClientInformationResponseMessage.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/FormOutboundClientInformationResponseMessage.java
new file mode 100644
index 00000000..73151370
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/FormOutboundClientInformationResponseMessage.java
@@ -0,0 +1,50 @@
+/*
+ * 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.impl;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.profile.action.ActionSupport;
+import org.opensaml.profile.action.EventIds;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
+import com.nimbusds.openid.connect.sdk.rp.OIDCClientInformation;
+import com.nimbusds.openid.connect.sdk.rp.OIDCClientInformationResponse;
+
+import net.shibboleth.shared.primitive.LoggerFactory;
+
+/**
+ * Action that forms outbound dynamic client registration response.
+ */
+public class FormOutboundClientInformationResponseMessage extends AbstractOIDCClientRegistrationAction {
+
+ /** Class logger. */
+ @Nonnull private Logger log = LoggerFactory.getLogger(FormOutboundClientInformationResponseMessage.class);
+
+ /** {@inheritDoc} */
+ @Override
+ protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
+ final OIDCClientInformation clientInformation = getRegistrationContext().getClientInformation();
+ if (clientInformation == null) {
+ log.error("{} Could not find client information from context data", getLogPrefix());
+ ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
+ return;
+ }
+ final OIDCClientInformationResponse response = new OIDCClientInformationResponse(clientInformation, true);
+ profileRequestContext.ensureOutboundMessageContext().setMessage(response);
+ log.info("{} Client information successfully added to the outbound context", getLogPrefix());
+ }
+
+}
\ 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 eef8bfaa..b74e4457 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
@@ -28,10 +28,10 @@ import org.slf4j.Logger;
import com.nimbusds.oauth2.sdk.client.ClientInformation;
import com.nimbusds.openid.connect.sdk.rp.OIDCClientInformation;
-import com.nimbusds.openid.connect.sdk.rp.OIDCClientInformationResponse;
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.profile.AbstractProfileAction;
@@ -60,16 +60,17 @@ public class StoreClientInformation extends AbstractProfileAction {
/** 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;
- /** The response message. */
- @Nullable private OIDCClientInformationResponse response;
-
/** Constructor. */
public StoreClientInformation() {
registrationTokenContextLookupStrategy = new DefaultOIDCClientRegistrationTokenClaimsContextLookupFunction();
+ clientInformationLookupStrategy = new DefaultClientInformationLookupFunction();
}
/**
@@ -116,6 +117,17 @@ public class StoreClientInformation extends AbstractProfileAction {
"OIDCClientRegistrationTokenClaimsContext lookup strategy cannot be null");
}
+ /**
+ * Set the strategy used to locate {@link OIDCClientInformation} to be stored.
+ *
+ * @param strategy lookup strategy
+ */
+ public void setClientInformationLookupStrategy(@Nonnull final Function<ProfileRequestContext,OIDCClientInformation> strategy) {
+ checkSetterPreconditions();
+
+ clientInformationLookupStrategy = Constraint.isNotNull(strategy, "Client information lookup strategy cannot be null");
+ }
+
/** {@inheritDoc} */
@Override
protected void doInitialize() throws ComponentInitializationException {
@@ -133,25 +145,11 @@ public class StoreClientInformation extends AbstractProfileAction {
return false;
}
- if (profileRequestContext.getOutboundMessageContext() == null) {
- log.error("{} Unable to locate outbound message context", getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MSG_CTX);
- return false;
- }
-
- final Object message = profileRequestContext.ensureOutboundMessageContext().getMessage();
- if (message == null || !(message instanceof OIDCClientInformationResponse)) {
- log.error("{} Unable to locate outbound message", getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MSG_CTX);
- return false;
- }
-
registrationTokenCtx = registrationTokenContextLookupStrategy.apply(profileRequestContext);
if (registrationTokenCtx != null && registrationTokenCtx.getClaimsSet() == null) {
registrationTokenCtx = null;
}
- response = (OIDCClientInformationResponse) message;
return true;
}
@@ -159,8 +157,7 @@ public class StoreClientInformation extends AbstractProfileAction {
@Override
protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
- assert response != null;
- final OIDCClientInformation clientInformation = response.getOIDCClientInformation();
+ final OIDCClientInformation clientInformation = clientInformationLookupStrategy.apply(profileRequestContext);
if (clientInformation == null) {
log.error("{} Unable to locate client information from the response message", getLogPrefix());
ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MSG_CTX);
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/abstract-register/oidc-abstract-register-beans.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/abstract-register/oidc-abstract-register-beans.xml
new file mode 100644
index 00000000..1d01bc04
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/abstract-register/oidc-abstract-register-beans.xml
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans" xmlns:c="http://www.springframework.org/schema/c"
+ xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
+ xmlns:util="http://www.springframework.org/schema/util" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
+ http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
+ default-init-method="initialize" default-destroy-method="destroy">
+
+ <bean id="shibboleth.oidc.browserProfile" class="java.lang.Boolean" c:_0="false" />
+
+ <bean id="InitializeOutboundMessageContext"
+ class="net.shibboleth.idp.plugin.oidc.op.profile.impl.InitializeOutboundRegistrationResponseMessageContext"
+ scope="prototype">
+ </bean>
+
+ <bean id="InitializeRelyingPartyContext"
+ class="net.shibboleth.idp.plugin.oidc.op.profile.impl.InitializeUnverifiedRelyingPartyContext"
+ scope="prototype" />
+
+ <bean id="AddRedirectUrisToClientMetadata"
+ class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddRedirectUrisToClientMetadata"
+ scope="prototype"
+ p:oidcInputMetadataLookupStrategy-ref="shibboleth.oidc.InputMetadataLookupStrategy"/>
+
+ <bean id="AddTokenEndpointAuthMethodsToClientMetadata"
+ class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddTokenEndpointAuthMethodsToClientMetadata"
+ scope="prototype"
+ p:oidcInputMetadataLookupStrategy-ref="shibboleth.oidc.InputMetadataLookupStrategy"
+ p:tokenEndpointAuthMethodsLookupStrategy-ref="shibboleth.oidc.TokenEndpointAuthMethodsLookupStrategy"/>
+
+ <bean id="AddApplicationTypeToClientMetadata"
+ class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddApplicationTypeToClientMetadata"
+ scope="prototype"
+ p:oidcInputMetadataLookupStrategy-ref="shibboleth.oidc.InputMetadataLookupStrategy"/>
+
+ <bean id="AddScopeToClientMetadata"
+ class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddScopeToClientMetadata"
+ scope="prototype"
+ p:oidcInputMetadataLookupStrategy-ref="shibboleth.oidc.InputMetadataLookupStrategy"
+ p:defaultScope-ref="shibboleth.oidc.DefaultScope" />
+
+ <bean id="AddContactsToClientMetadata"
+ class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddContactsToClientMetadata"
+ scope="prototype"
+ p:oidcInputMetadataLookupStrategy-ref="shibboleth.oidc.InputMetadataLookupStrategy"/>
+
+ <bean id="AddGrantTypeToClientMetadata"
+ class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddGrantTypeToClientMetadata"
+ scope="prototype"
+ p:oidcInputMetadataLookupStrategy-ref="shibboleth.oidc.InputMetadataLookupStrategy"/>
+
+ <bean id="AddSubjectTypeToClientMetadata"
+ class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddSubjectTypeToClientMetadata"
+ scope="prototype"
+ p:oidcInputMetadataLookupStrategy-ref="shibboleth.oidc.InputMetadataLookupStrategy"
+ p:defaultSubjectType-ref="shibboleth.oidc.DefaultSubjectType" />
+
+ <bean id="AddLogoUrisToClientMetadata"
+ class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddLogoUrisToClientMetadata"
+ scope="prototype"
+ p:oidcInputMetadataLookupStrategy-ref="shibboleth.oidc.InputMetadataLookupStrategy"/>
+
+ <bean id="AddPolicyUrisToClientMetadata"
+ class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddPolicyUrisToClientMetadata"
+ scope="prototype"
+ p:oidcInputMetadataLookupStrategy-ref="shibboleth.oidc.InputMetadataLookupStrategy"/>
+
+ <bean id="AddTosUrisToClientMetadata"
+ class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddTosUrisToClientMetadata"
+ scope="prototype"
+ p:oidcInputMetadataLookupStrategy-ref="shibboleth.oidc.InputMetadataLookupStrategy"/>
+
+ <bean id="AddClientNameToClientMetadata"
+ class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddClientNameToClientMetadata"
+ scope="prototype"
+ p:oidcInputMetadataLookupStrategy-ref="shibboleth.oidc.InputMetadataLookupStrategy"/>
+
+ <bean id="AddSecurityConfigurationToClientMetadata"
+ class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddSecurityConfigurationToClientMetadata"
+ scope="prototype"
+ p:oidcInputMetadataLookupStrategy-ref="shibboleth.oidc.InputMetadataLookupStrategy"/>
+
+ <bean id="AddRequestObjectSecurityConfigurationToClientMetadata"
+ class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddRequestObjectSecurityConfigurationToClientMetadata"
+ p:allowSignatureNone="%{idp.oidc.dynreg.allowNoneForRequestSigning:true}"
+ scope="prototype"
+ p:oidcInputMetadataLookupStrategy-ref="shibboleth.oidc.InputMetadataLookupStrategy"/>
+
+ <bean id="AddRequestUrisToClientMetadata"
+ class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddRequestUrisToClientMetadata"
+ scope="prototype"
+ p:oidcInputMetadataLookupStrategy-ref="shibboleth.oidc.InputMetadataLookupStrategy"/>
+
+ <bean id="AddLogoutParametersToClientMetadata"
+ class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddLogoutParametersToClientMetadata"
+ scope="prototype"
+ p:oidcInputMetadataLookupStrategy-ref="shibboleth.oidc.InputMetadataLookupStrategy"/>
+
+ <bean id="AddRemainingClaimsToClientMetadata"
+ class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddRemainingClaimsToClientMetadata"
+ scope="prototype"
+ p:oidcInputMetadataLookupStrategy-ref="shibboleth.oidc.InputMetadataLookupStrategy"/>
+
+ <bean id="AddResponseTypesToClientMetadata"
+ class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddResponseTypesToClientMetadata"
+ scope="prototype"
+ p:oidcInputMetadataLookupStrategy-ref="shibboleth.oidc.InputMetadataLookupStrategy"/>
+
+ <bean id="BuildClientInformation"
+ class="net.shibboleth.idp.plugin.oidc.op.profile.impl.BuildClientInformation"
+ scope="prototype" />
+
+ <bean id="oidc.messageEncoderFactory"
+ class="net.shibboleth.idp.plugin.oidc.op.encoding.impl.OIDCResponseEncoderFactory"
+ p:messageEncoder-ref="oidc.nimbusEncoder" scope="prototype" />
+
+ <bean id="oidc.nimbusEncoder"
+ class="net.shibboleth.idp.plugin.oidc.op.encoding.impl.NimbusResponseEncoder"
+ scope="prototype"
+ p:httpServletResponseSupplier-ref="shibboleth.HttpServletResponseSupplier"
+ p:objectMapper-ref="#{'%{idp.oidc.logging.objectMapper:shibboleth.oidc.JSONObjectMapper}'.trim()}"
+ init-method="" />
+
+ <bean id="EncodeMessage"
+ class="org.opensaml.profile.action.impl.EncodeMessage"
+ scope="prototype"
+ p:messageEncoderFactory-ref="oidc.messageEncoderFactory"
+ p:httpServletResponseSupplier-ref="shibboleth.HttpServletResponseSupplier" />
+
+</beans>
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/register/register-flow.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/abstract-register/oidc-abstract-register-flow.xml
similarity index 66%
copy from idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/register/register-flow.xml
copy to idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/abstract-register/oidc-abstract-register-flow.xml
index 637c5620..8c510e5c 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/register/register-flow.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/abstract-register/oidc-abstract-register-flow.xml
@@ -8,21 +8,6 @@
<evaluate expression="PopulateMetricContext" />
<evaluate expression="FlowStartPopulateAuditContext" />
<evaluate expression="InitializeOutboundMessageContext" />
- <evaluate expression="'proceed'" />
- <transition on="proceed" to="DecodeMessage">
- <set name="flowScope.transitionAfterDecode" value="'PostDecodeMessage'" />
- </transition>
- </action-state>
-
- <action-state id="PostDecodeMessage">
- <evaluate expression="InitializeRelyingPartyContext" />
- <evaluate expression="ValidateRegistrationAccessToken" />
- <evaluate expression="SelectRelyingPartyConfiguration" />
- <evaluate expression="SelectProfileConfiguration" />
- <evaluate expression="PopulateInboundInterceptContext" />
- <evaluate expression="'proceed'" />
- <transition on="proceed"
- to="CheckInboundInterceptContext" />
</action-state>
<decision-state id="CheckInboundInterceptContext">
@@ -35,14 +20,6 @@
<transition on="proceed" to="OutboundContextsAndSecurityParameters" />
</subflow-state>
- <action-state id="OutboundContextsAndSecurityParameters">
- <evaluate expression="InitializeRegistrationMetadataPolicyContext" />
- <evaluate expression="ValidateRegistrationRequestMetadata" />
- <evaluate expression="CheckRedirectURIs" />
- <evaluate expression="'proceed'" />
- <transition on="proceed" to="BuildResponse" />
- </action-state>
-
<action-state id="BuildResponse">
<evaluate expression="GenerateClientID" />
<evaluate expression="GenerateClientSecret" />
@@ -64,20 +41,18 @@
<evaluate expression="AddRequestUrisToClientMetadata" />
<evaluate expression="AddLogoutParametersToClientMetadata" />
<evaluate expression="AddRemainingClaimsToClientMetadata" />
- <evaluate expression="'proceed'" />
- <transition on="proceed" to="BuildResponseMessage" />
- </action-state>
-
- <action-state id="BuildResponseMessage">
- <transition on="proceed" to="StoreClientInformation" />
+ <evaluate expression="BuildClientInformation" />
</action-state>
<action-state id="StoreClientInformation">
+ <on-entry>
+ <set name="flowScope.transitionAfterOutboundIntercept" value="#null" />
+ </on-entry>
<evaluate expression="StoreClientInformation" />
<evaluate expression="'proceed'" />
<transition on="proceed" to="CommitResponse" />
</action-state>
- <bean-import resource="register-beans.xml" />
+ <bean-import resource="classpath:/META-INF/net/shibboleth/idp/flows/oidc/abstract-register/oidc-abstract-register-beans.xml" />
</flow>
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/abstract/oidc-abstract-flow.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/abstract/oidc-abstract-flow.xml
index 4b91bbdc..67fcc15b 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/abstract/oidc-abstract-flow.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/abstract/oidc-abstract-flow.xml
@@ -41,15 +41,16 @@
<decision-state id="CheckOutboundInterceptContext">
<on-entry>
+ <evaluate expression="flowRequestContext.getFlowScope().get('transitionAfterOutboundIntercept') != null ? flowRequestContext.getFlowScope().get('transitionAfterOutboundIntercept') : 'CommitResponse'" result="flowScope.postOutboundInterceptTransition"/>
<evaluate expression="PopulateOutboundInterceptContext" />
</on-entry>
<if test="opensamlProfileRequestContext.ensureSubcontext(T(net.shibboleth.idp.profile.context.ProfileInterceptorContext)).getAvailableFlows().isEmpty()"
- then="CommitResponse" else="DoOutboundInterceptSubflow" />
+ then="#{postOutboundInterceptTransition}" else="DoOutboundInterceptSubflow" />
</decision-state>
<subflow-state id="DoOutboundInterceptSubflow" subflow="intercept">
<input name="calledAsSubflow" value="true" />
- <transition on="proceed" to="CommitResponse" />
+ <transition on="proceed" to="#{postOutboundInterceptTransition}" />
<transition to="HandleError" />
</subflow-state>
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 0795687d..141cc452 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
@@ -19,11 +19,6 @@
<util:constant id="shibboleth.metrics.ProfileCounter"
static-field="net.shibboleth.oidc.profile.config.impl.DefaultOIDCDynamicRegistrationConfiguration.PROFILE_COUNTER" />
- <bean id="InitializeOutboundMessageContext"
- class="net.shibboleth.idp.plugin.oidc.op.profile.impl.InitializeOutboundRegistrationResponseMessageContext"
- scope="prototype">
- </bean>
-
<bean id="DecodeMessage" class="org.opensaml.profile.action.impl.DecodeMessage" scope="prototype">
<constructor-arg>
<bean
@@ -36,10 +31,6 @@
</constructor-arg>
</bean>
- <bean id="InitializeRelyingPartyContext"
- class="net.shibboleth.idp.plugin.oidc.op.profile.impl.InitializeUnverifiedRelyingPartyContext"
- scope="prototype" />
-
<bean id="ValidateRegistrationAccessToken"
class="net.shibboleth.idp.plugin.oidc.op.profile.impl.ValidateRegistrationAccessToken" scope="prototype"
p:revocationCache-ref="shibboleth.oidc.RevocationCache"
@@ -85,22 +76,11 @@
</property>
</bean>
- <bean id="AddRedirectUrisToClientMetadata"
- class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddRedirectUrisToClientMetadata"
- scope="prototype" />
-
- <bean id="AddTokenEndpointAuthMethodsToClientMetadata"
- class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddTokenEndpointAuthMethodsToClientMetadata"
- scope="prototype" />
+ <bean id="shibboleth.oidc.InputMetadataLookupStrategy"
+ class="net.shibboleth.idp.plugin.oidc.op.messaging.context.navigate.OIDCPolicyEnforcedClientRegistrationRequestMetadataLookupFunction" />
- <bean id="AddApplicationTypeToClientMetadata"
- class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddApplicationTypeToClientMetadata"
- scope="prototype" />
-
- <bean id="AddScopeToClientMetadata"
- class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddScopeToClientMetadata"
- scope="prototype"
- p:defaultScope-ref="shibboleth.oidc.DefaultScope" />
+ <bean id="shibboleth.oidc.TokenEndpointAuthMethodsLookupStrategy"
+ class="net.shibboleth.oidc.profile.config.navigate.TokenEndpointAuthMethodLookupFunction" />
<bean id="shibboleth.oidc.DefaultScope"
class="com.nimbusds.oauth2.sdk.Scope" factory-method="parse">
@@ -108,19 +88,6 @@
value="#{'%{idp.oidc.dynreg.defaultScope:openid profile email address phone offline_access}'.trim()}" />
</bean>
- <bean id="AddContactsToClientMetadata"
- class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddContactsToClientMetadata"
- scope="prototype" />
-
- <bean id="AddGrantTypeToClientMetadata"
- class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddGrantTypeToClientMetadata"
- scope="prototype" />
-
- <bean id="AddSubjectTypeToClientMetadata"
- class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddSubjectTypeToClientMetadata"
- scope="prototype"
- p:defaultSubjectType-ref="shibboleth.oidc.DefaultSubjectType" />
-
<bean id="shibboleth.oidc.DefaultSubjectType"
class="com.nimbusds.openid.connect.sdk.SubjectType"
factory-method="parse">
@@ -128,50 +95,14 @@
value="#{'%{idp.oidc.dynreg.defaultSubjectType:public}'.trim()}" />
</bean>
- <bean id="AddResponseTypesToClientMetadata"
- class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddResponseTypesToClientMetadata"
- scope="prototype" />
-
<bean id="AddJwksToClientMetadata"
class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddJwksToClientMetadata"
scope="prototype"
p:httpClient="#{getObject('shibboleth.oidc.NonBrowser.HttpClient') ?: getObject('shibboleth.InternalHttpClient')}"
p:httpClientSecurityParameters="#{getObject('shibboleth.oidc.NonBrowser.HttpClientSecurityParameters')}"
+ p:oidcInputMetadataLookupStrategy-ref="shibboleth.oidc.InputMetadataLookupStrategy"
p:validateRemoteJwkSetPredicate-ref="%{idp.oidc.dynreg.validateRemoteJwks:shibboleth.Conditions.TRUE}"/>
- <bean id="AddLogoUrisToClientMetadata"
- class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddLogoUrisToClientMetadata"
- scope="prototype" />
-
- <bean id="AddPolicyUrisToClientMetadata"
- class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddPolicyUrisToClientMetadata"
- scope="prototype" />
-
- <bean id="AddTosUrisToClientMetadata"
- class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddTosUrisToClientMetadata"
- scope="prototype" />
-
- <bean id="AddClientNameToClientMetadata"
- class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddClientNameToClientMetadata"
- scope="prototype" />
-
- <bean id="AddSecurityConfigurationToClientMetadata"
- class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddSecurityConfigurationToClientMetadata"
- scope="prototype" />
-
- <bean id="AddRequestObjectSecurityConfigurationToClientMetadata"
- class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddRequestObjectSecurityConfigurationToClientMetadata"
- p:allowSignatureNone="%{idp.oidc.dynreg.allowNoneForRequestSigning:true}" scope="prototype" />
-
- <bean id="AddRequestUrisToClientMetadata" scope="prototype"
- class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddRequestUrisToClientMetadata" />
-
- <bean id="AddLogoutParametersToClientMetadata" scope="prototype"
- class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddLogoutParametersToClientMetadata" />
-
- <bean id="AddRemainingClaimsToClientMetadata" scope="prototype"
- class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddRemainingClaimsToClientMetadata" />
-
<bean id="StoreClientInformation"
class="net.shibboleth.idp.plugin.oidc.op.profile.impl.StoreClientInformation" scope="prototype"
p:clientInformationManager-ref="#{'%{idp.oidc.dynreg.clientInformationManager:shibboleth.oidc.ClientInformationManager}'.trim()}">
@@ -192,26 +123,9 @@
</bean>
<bean id="FormOutboundMessage"
- class="net.shibboleth.idp.plugin.oidc.op.profile.impl.BuildClientInformation"
+ class="net.shibboleth.idp.plugin.oidc.op.profile.impl.FormOutboundClientInformationResponseMessage"
scope="prototype" />
- <bean id="oidc.messageEncoderFactory"
- class="net.shibboleth.idp.plugin.oidc.op.encoding.impl.OIDCResponseEncoderFactory"
- p:messageEncoder-ref="oidc.nimbusEncoder" scope="prototype" />
-
- <bean id="oidc.nimbusEncoder"
- class="net.shibboleth.idp.plugin.oidc.op.encoding.impl.NimbusResponseEncoder"
- scope="prototype"
- p:httpServletResponseSupplier-ref="shibboleth.HttpServletResponseSupplier"
- p:objectMapper-ref="#{'%{idp.oidc.logging.objectMapper:shibboleth.oidc.JSONObjectMapper}'.trim()}"
- init-method="" />
-
- <bean id="EncodeMessage"
- class="org.opensaml.profile.action.impl.EncodeMessage"
- scope="prototype"
- p:messageEncoderFactory-ref="oidc.messageEncoderFactory"
- p:httpServletResponseSupplier-ref="shibboleth.HttpServletResponseSupplier" />
-
<bean id="PostResponsePopulateAuditContext" parent="shibboleth.AbstractPopulateAuditContext"
p:fieldExtractors="#{getObject('shibboleth.oidc.RegistrationPostResponseAuditExtractors') ?: getObject('shibboleth.oidc.DefaultRegistrationPostResponseAuditExtractors')}" />
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/register/register-flow.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/register/register-flow.xml
index 637c5620..2b0d0041 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/register/register-flow.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/register/register-flow.xml
@@ -1,13 +1,9 @@
<flow xmlns="http://www.springframework.org/schema/webflow"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow.xsd"
- parent="oidc/abstract-api">
+ parent="oidc/abstract-register">
<action-state id="InitializeMandatoryContexts">
- <evaluate expression="InitializeProfileRequestContext" />
- <evaluate expression="PopulateMetricContext" />
- <evaluate expression="FlowStartPopulateAuditContext" />
- <evaluate expression="InitializeOutboundMessageContext" />
<evaluate expression="'proceed'" />
<transition on="proceed" to="DecodeMessage">
<set name="flowScope.transitionAfterDecode" value="'PostDecodeMessage'" />
@@ -21,20 +17,9 @@
<evaluate expression="SelectProfileConfiguration" />
<evaluate expression="PopulateInboundInterceptContext" />
<evaluate expression="'proceed'" />
- <transition on="proceed"
- to="CheckInboundInterceptContext" />
+ <transition on="proceed" to="CheckInboundInterceptContext" />
</action-state>
- <decision-state id="CheckInboundInterceptContext">
- <if test="opensamlProfileRequestContext.ensureSubcontext(T(net.shibboleth.idp.profile.context.ProfileInterceptorContext)).getAvailableFlows().isEmpty()"
- then="OutboundContextsAndSecurityParameters" else="DoInboundInterceptSubflow" />
- </decision-state>
-
- <subflow-state id="DoInboundInterceptSubflow" subflow="intercept">
- <input name="calledAsSubflow" value="true" />
- <transition on="proceed" to="OutboundContextsAndSecurityParameters" />
- </subflow-state>
-
<action-state id="OutboundContextsAndSecurityParameters">
<evaluate expression="InitializeRegistrationMetadataPolicyContext" />
<evaluate expression="ValidateRegistrationRequestMetadata" />
@@ -44,38 +29,10 @@
</action-state>
<action-state id="BuildResponse">
- <evaluate expression="GenerateClientID" />
- <evaluate expression="GenerateClientSecret" />
- <evaluate expression="AddRedirectUrisToClientMetadata" />
- <evaluate expression="AddApplicationTypeToClientMetadata" />
- <evaluate expression="AddScopeToClientMetadata" />
- <evaluate expression="AddGrantTypeToClientMetadata" />
- <evaluate expression="AddResponseTypesToClientMetadata" />
- <evaluate expression="AddSubjectTypeToClientMetadata" />
- <evaluate expression="AddContactsToClientMetadata" />
- <evaluate expression="AddJwksToClientMetadata" />
- <evaluate expression="AddTokenEndpointAuthMethodsToClientMetadata" />
- <evaluate expression="AddLogoUrisToClientMetadata" />
- <evaluate expression="AddPolicyUrisToClientMetadata" />
- <evaluate expression="AddTosUrisToClientMetadata" />
- <evaluate expression="AddClientNameToClientMetadata" />
- <evaluate expression="AddSecurityConfigurationToClientMetadata" />
- <evaluate expression="AddRequestObjectSecurityConfigurationToClientMetadata" />
- <evaluate expression="AddRequestUrisToClientMetadata" />
- <evaluate expression="AddLogoutParametersToClientMetadata" />
- <evaluate expression="AddRemainingClaimsToClientMetadata" />
- <evaluate expression="'proceed'" />
- <transition on="proceed" to="BuildResponseMessage" />
- </action-state>
-
- <action-state id="BuildResponseMessage">
- <transition on="proceed" to="StoreClientInformation" />
- </action-state>
-
- <action-state id="StoreClientInformation">
- <evaluate expression="StoreClientInformation" />
<evaluate expression="'proceed'" />
- <transition on="proceed" to="CommitResponse" />
+ <transition on="proceed" to="FormResponseMessage">
+ <set name="flowScope.transitionAfterOutboundIntercept" value="'StoreClientInformation'" />
+ </transition>
</action-state>
<bean-import resource="register-beans.xml" />
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidfed/entity-configuration/entity-configuration-beans.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidfed/entity-configuration/entity-configuration-beans.xml
index a4dbb7d6..c6c06ee3 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidfed/entity-configuration/entity-configuration-beans.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidfed/entity-configuration/entity-configuration-beans.xml
@@ -68,7 +68,7 @@
c:f-ref="shibboleth.MessageContextLookup.Outbound" />
<bean id="BuildEntityStatement"
- class="net.shibboleth.idp.plugin.oidc.op.oidfed.profile.impl.BuildEntityStatement" scope="prototype"
+ class="net.shibboleth.idp.plugin.oidc.op.oidfed.profile.impl.BuildEntityConfiguration" scope="prototype"
p:identifierGeneratorLookupStrategy-ref="shibboleth.oidc.DefaultIdentifierGenerationStrategy"
p:objectMapper-ref="#{'%{idp.oidc.logging.objectMapper:shibboleth.oidc.JSONObjectMapper}'.trim()}" />
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
new file mode 100644
index 00000000..2b0bac38
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidfed/register/register-beans.xml
@@ -0,0 +1,264 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:c="http://www.springframework.org/schema/c"
+ xmlns:context="http://www.springframework.org/schema/context"
+ xmlns:p="http://www.springframework.org/schema/p"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
+ http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
+ default-init-method="initialize" default-destroy-method="destroy">
+
+ <bean id="shibboleth.oidc.profileId" class="java.lang.String"
+ c:_0="#{T(net.shibboleth.idp.plugin.oidc.op.oidfed.config.OIDFederationExplicitRegistrationConfiguration).PROFILE_ID}" />
+
+ <bean id="shibboleth.oidc.loggingId" class="java.lang.String"
+ c:_0="%{idp.service.logging.oidfeddynreg:OIDFED.Registration}" />
+
+ <bean id="shibboleth.ClientIDLookupStrategy"
+ class="net.shibboleth.idp.plugin.oidc.op.oidfed.profile.navigate.ExplicitClientRegistrationRequestClientIDLookupFunction" />
+
+ <util:constant id="shibboleth.metrics.ProfileCounter"
+ static-field="net.shibboleth.oidc.profile.config.impl.DefaultOIDCDynamicRegistrationConfiguration.PROFILE_COUNTER" />
+
+ <bean id="DecodeMessage" class="org.opensaml.profile.action.impl.DecodeMessage" scope="prototype">
+ <constructor-arg>
+ <bean
+ class="net.shibboleth.idp.plugin.oidc.op.oidfed.decoding.impl.ExplicitClientRegistrationRequestDecoder"
+ scope="prototype"
+ p:httpServletRequestSupplier-ref="shibboleth.HttpServletRequestSupplier"
+ p:removeIpAddressFromEndpointUri="%{idp.oidc.logging.removeIpAddressFromProtocolMessage:false}"
+ p:objectMapper-ref="#{'%{idp.oidc.logging.objectMapper:shibboleth.oidc.JSONObjectMapper}'.trim()}"
+ p:customRequestParser="#{getObject('%{idp.oidc.requestParser.FederationRegisterRequest:}'.trim())}"/>
+ </constructor-arg>
+ </bean>
+
+ <bean id="ExplicitRegistrationRelyingPartyCreationStrategy" parent="shibboleth.Functions.Expression"
+ c:expression="#input.ensureSubcontext(T(net.shibboleth.profile.context.RelyingPartyContext))" />
+
+ <bean id="ResolveTrustChains" class="net.shibboleth.idp.plugin.oidc.op.oidfed.profile.impl.ResolveTrustChains"
+ scope="prototype"
+ p:trustChainCache-ref="#{'%{idp.oidfed.authorize.TrustChainMetadataCache:shibboleth.oidfed.TrustChainMetadataCache}'.trim()}"
+ p:clientIDLookupStrategy-ref="shibboleth.ClientIDLookupStrategy"
+ p:metadataPolicyMergingStrategy-ref="#{'%{idp.oidfed.authorize.TrustChainMetadataPolicyMergingStrategy:DefaultTrustChainMetadataPolicyMergingStrategy}'.trim()}"
+ p:metadataPolicyEnforcer-ref="#{'%{idp.oidfed.authorize.MetadataPolicyEnforcer:DefaultMetadataPolicyEnforcer}'.trim()}"
+ p:preSelectedTrustChainIdsLookupStrategy="#{getObject('shibboleth.oidfed.PreSelectedTrustChainIDsLookupStrategy') ?: getObject('shibboleth.oidfed.DefaultPreSelectedTrustChainIDsLookupStrategy')}"/>
+
+ <bean id="DefaultMetadataPolicyEnforcer"
+ class="net.shibboleth.oidc.metadata.policy.impl.DefaultMetadataPolicyEnforcer" />
+
+ <bean id="DefaultTrustChainMetadataPolicyMergingStrategy"
+ class="net.shibboleth.idp.plugin.oidc.op.oidfed.profile.navigate.DefaultTrustChainMetadataPolicyMergingStrategy"
+ p:localMetadataPolicyStrategy-ref="#{'%{idp.oidfed.authorize.LocalMetadataPolicyStrategy:DefaultLocalMetadataPolicyStrategy}'.trim()}"/>
+
+ <bean id="DefaultLocalMetadataPolicyStrategy"
+ parent="shibboleth.Functions.Constant">
+ <constructor-arg name="target">
+ <util:map>
+ <entry key="scope">
+ <bean class="net.shibboleth.oidc.metadata.policy.MetadataPolicy" p:defaultValue="openid" />
+ </entry>
+ <entry key="token_endpoint_auth_method">
+ <bean class="net.shibboleth.oidc.metadata.policy.MetadataPolicy" p:defaultValue="private_key_jwt" />
+ </entry>
+ </util:map>
+ </constructor-arg>
+ </bean>
+
+ <bean id="SelectTrustChain" class="net.shibboleth.idp.plugin.oidc.op.oidfed.profile.impl.SelectTrustChain"
+ scope="prototype"
+ p:relyingPartyContextCreationStrategy-ref="ExplicitRegistrationRelyingPartyCreationStrategy">
+ <property name="activationCondition">
+ <bean parent="shibboleth.Conditions.Expression"
+ c:expression="#input.ensureInboundMessageContext().containsSubcontext(T(net.shibboleth.idp.plugin.oidc.op.oidfed.profile.impl.RelyingPartyTrustChainContext))" />
+ </property>
+ </bean>
+
+ <bean id="ResolveTrustMarks" class="net.shibboleth.idp.plugin.oidc.op.oidfed.profile.impl.ResolveTrustMarks"
+ scope="prototype"
+ p:trustChainCache-ref="#{'%{idp.oidfed.authorize.TrustChainMetadataCache:shibboleth.oidfed.TrustChainMetadataCache}'.trim()}">
+ <property name="trustEngine">
+ <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.TokenPayloadAsymmetricKeyTrustEngine">
+ <constructor-arg>
+ <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.DefaultEntityStatementCredentialResolver" />
+ </constructor-arg>
+ </bean>
+ </property>
+ <property name="trustedTrustMarkIssuersLookupStrategy">
+ <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.DefaultTrustChainTrustedTrustMarkIssuersLookupStrategy"
+ p:objectMapper-ref="shibboleth.oidc.JSONObjectMapper" />
+ </property>
+ </bean>
+
+ <bean id="RelyingPartyTrustChainContextLookupStrategy" parent="shibboleth.Functions.Expression"
+ c:expression="#input.ensureInboundMessageContext().getSubcontext(T(net.shibboleth.idp.plugin.oidc.op.oidfed.profile.impl.RelyingPartyTrustChainContext))" />
+
+ <bean id="SelectExplicitRegistrationRelyingPartyConfiguration"
+ class="net.shibboleth.idp.profile.impl.SelectRelyingPartyConfiguration" scope="prototype"
+ p:relyingPartyContextLookupStrategy-ref="ExplicitRegistrationRelyingPartyCreationStrategy"
+ p:relyingPartyConfigurationResolver-ref="shibboleth.RelyingPartyResolverService" />
+
+ <bean id="SelectExplicitRegistrationProfileConfiguration"
+ class="net.shibboleth.idp.profile.impl.SelectProfileConfiguration" scope="prototype"
+ p:relyingPartyContextLookupStrategy-ref="ExplicitRegistrationRelyingPartyCreationStrategy"
+ p:profileId="#{T(net.shibboleth.idp.plugin.oidc.op.oidfed.config.OIDFederationExplicitRegistrationConfiguration).PROFILE_ID}" />
+
+ <bean id="InitializeRegistrationMetadataPolicyContext"
+ class="net.shibboleth.idp.plugin.oidc.op.profile.impl.InitializeRegistrationMetadataPolicyContext"
+ scope="prototype"
+ p:metadataPolicyValidationStrategy="#{getObject('shibboleth.oidc.dynreg.MetadataPolicyValidator') ?: getObject('shibboleth.oidc.dynreg.DefaultMetadataPolicyValidator')}" />
+
+ <bean id="ValidateExplicitRegistrationProfileConfiguration"
+ class="net.shibboleth.idp.plugin.oidc.op.oidfed.profile.impl.ValidateAutomaticRegistrationProfileConfiguration"
+ scope="prototype">
+ <property name="localMetadataPolicyMergingStrategy">
+ <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.profile.navigate.DefaultLocalMetadataPolicyMergingStrategy"
+ p:metadataPolicyEnforcer-ref="#{'%{idp.oidfed.authorize.MetadataPolicyEnforcer:DefaultMetadataPolicyEnforcer}'.trim()}"/>
+ </property>
+ <property name="mandatoryTrustMarksLookupStrategy">
+ <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.profile.navigate.MandatoryTrustMarksLookupFunction"
+ p:relyingPartyContextLookupStrategy-ref="ExplicitRegistrationRelyingPartyCreationStrategy"/>
+ </property>
+ </bean>
+
+ <bean id="ValidateRegistrationRequestMetadata"
+ class="net.shibboleth.idp.plugin.oidc.op.profile.impl.ValidateRegistrationRequestMetadata"
+ scope="prototype"
+ p:metadataPolicyEnforcer="#{getObject('shibboleth.oidc.dynreg.MetadataPolicyEnforcer') ?: getObject('shibboleth.oidc.dynreg.DefaultMetadataPolicyEnforcer')}"/>
+
+ <bean id="CheckRedirectURIs"
+ class="net.shibboleth.idp.plugin.oidc.op.profile.impl.CheckRedirectURIs"
+ scope="prototype"
+ p:httpClient="#{getObject('shibboleth.oidc.NonBrowser.HttpClient') ?: getObject('shibboleth.InternalHttpClient')}"
+ p:httpClientSecurityParameters="#{getObject('shibboleth.oidc.NonBrowser.HttpClientSecurityParameters')}"
+ p:objectMapper-ref="shibboleth.oidc.JSONObjectMapper">
+ <property name="requestMetadataLookupStrategy">
+ <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.profile.navigate.DefaultSelectedTrustChainMetadataLookupStrategy" />
+ </property>
+ </bean>
+
+ <bean id="GenerateClientID"
+ class="net.shibboleth.idp.plugin.oidc.op.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')}"/>
+
+ <bean id="GenerateClientSecret"
+ class="net.shibboleth.idp.plugin.oidc.op.profile.impl.GenerateClientSecret" scope="prototype"
+ p:xmlSafeIdentifier="%{idp.oidc.xmlSafeIdentifiers:true}"
+ p:identifierGeneratorLookupStrategy="#{getObject('shibboleth.oidc.dynreg.ClientSecretGenerationStrategy') ?: getObject('shibboleth.oidc.DefaultIdentifierGenerationStrategy')}">
+ <property name="secretExpirationPeriodStrategy">
+ <bean class="net.shibboleth.oidc.profile.config.navigate.SecretExpirationPeriodLookupFunction" />
+ </property>
+ </bean>
+
+ <bean id="shibboleth.oidc.InputMetadataLookupStrategy"
+ class="net.shibboleth.idp.plugin.oidc.op.oidfed.profile.navigate.DefaultSelectedTrustChainMetadataLookupStrategy" />
+
+ <bean id="shibboleth.oidc.TokenEndpointAuthMethodsLookupStrategy"
+ class="net.shibboleth.idp.plugin.oidc.op.oidfed.config.TokenEndpointAuthMethodLookupFunction" />
+
+ <bean id="shibboleth.oidc.DefaultScope"
+ class="com.nimbusds.oauth2.sdk.Scope" factory-method="parse">
+ <constructor-arg type="java.lang.String"
+ value="#{'%{idp.oidfed.expreg.defaultScope:openid profile email address phone offline_access}'.trim()}" />
+ </bean>
+
+ <bean id="shibboleth.oidc.DefaultSubjectType"
+ class="com.nimbusds.openid.connect.sdk.SubjectType"
+ factory-method="parse">
+ <constructor-arg type="java.lang.String"
+ value="#{'%{idp.oidfed.expreg.defaultSubjectType:public}'.trim()}" />
+ </bean>
+
+ <bean id="AddJwksToClientMetadata"
+ class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddJwksToClientMetadata"
+ scope="prototype"
+ p:httpClient="#{getObject('shibboleth.oidc.NonBrowser.HttpClient') ?: getObject('shibboleth.InternalHttpClient')}"
+ p:httpClientSecurityParameters="#{getObject('shibboleth.oidc.NonBrowser.HttpClientSecurityParameters')}"
+ p:oidcInputMetadataLookupStrategy-ref="shibboleth.oidc.InputMetadataLookupStrategy"
+ p:validateRemoteJwkSetPredicate-ref="%{idp.oidfed.expreg.validateRemoteJwks:shibboleth.Conditions.TRUE}"/>
+
+ <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()}">
+ <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())" />
+ </property>
+ </bean>
+
+ <bean id="BuildErrorResponseFromEvent"
+ class="net.shibboleth.idp.plugin.oidc.op.profile.impl.BuildRegistrationErrorResponseFromEvent"
+ scope="prototype"
+ p:httpServletResponseSupplier-ref="shibboleth.HttpServletResponseSupplier"
+ p:mappedErrors="#{getObject('shibboleth.oidc.register.MappedErrors') ?: getObject('shibboleth.oidc.register.DefaultMappedErrors')}">
+ <property name="eventContextLookupStrategy">
+ <bean
+ class="net.shibboleth.idp.profile.context.navigate.WebFlowCurrentEventLookupFunction" />
+ </property>
+ </bean>
+
+ <bean id="PopulateEntityStatementSignatureSigningParameters"
+ class="net.shibboleth.oidc.profile.impl.PopulateJWTSignatureSigningParameters" scope="prototype"
+ c:strategy-ref="shibboleth.MessageContextLookup.Outbound"
+ p:securityParametersContextLookupStrategy-ref="EntityStatementSecurityParametersContextLookupStrategy">
+ <property name="configurationLookupStrategy">
+ <bean lazy-init="true"
+ class="net.shibboleth.oidc.profile.config.navigate.JWTSignatureSigningConfigurationLookupFunction" />
+ </property>
+ <property name="signatureSigningParametersResolver">
+ <bean class="net.shibboleth.oidc.security.jose.impl.ClientInformationSignatureSigningParametersResolver">
+ <constructor-arg name="signatureAlgorithmLookupStrategy">
+ <bean parent="shibboleth.Functions.Constant" c:target="" />
+ </constructor-arg>
+ <constructor-arg name="defaultAlgorithmValue" value="%{idp.oidfed.entity.sigalg:RS256}" />
+ </bean>
+ </property>
+ </bean>
+
+ <bean id="EntityStatementSecurityParametersContextLookupStrategy" parent="shibboleth.Functions.Compose"
+ c:g-ref="shibboleth.oidc.ChildLookupOrCreate.JWTSecurityParameters"
+ c:f-ref="shibboleth.ChildLookup.RelyingParty" />
+
+ <bean id="EntityStatementSecurityParametersCreationViaMessageContextStrategy" parent="shibboleth.Functions.Compose">
+ <constructor-arg name="g" ref="EntityStatementSecurityParametersContextLookupStrategy" />
+ <constructor-arg name="f">
+ <bean parent="shibboleth.Functions.Expression" c:expression="#input.getParent()" />
+ </constructor-arg>
+ </bean>
+
+ <bean id="BuildEntityStatement"
+ class="net.shibboleth.idp.plugin.oidc.op.oidfed.profile.impl.BuildExplicitRegistrationResponse" scope="prototype"
+ p:identifierGeneratorLookupStrategy-ref="shibboleth.oidc.DefaultIdentifierGenerationStrategy"
+ p:objectMapper-ref="#{'%{idp.oidc.logging.objectMapper:shibboleth.oidc.JSONObjectMapper}'.trim()}" />
+
+ <bean id="SignEntityStatement" class="net.shibboleth.idp.profile.impl.WebFlowMessageHandlerAdaptor"
+ scope="prototype" c:executionDirection="OUTBOUND ">
+ <constructor-arg name="messageHandler">
+ <bean id="SignEntityStatementHandler"
+ class="net.shibboleth.oidc.security.impl.SignJWTHandler" scope="prototype" p:logName="Entity Statement"
+ p:securityParametersLookupStrategy-ref="EntityStatementSecurityParametersCreationViaMessageContextStrategy"
+ p:typeHeader="entity-statement+jwt">
+ <property name="claimsToSignLookupStrategy">
+ <bean
+ class="net.shibboleth.idp.plugin.oidc.op.oidfed.profile.impl.JWTClaimsSetFromEntityStatementLookupFunction" />
+ </property>
+ <property name="jwtUpdateConsumer">
+ <bean
+ class="net.shibboleth.idp.plugin.oidc.op.oidfed.profile.impl.EntityStatementUpdateStrategy" />
+ </property>
+ </bean>
+ </constructor-arg>
+ </bean>
+
+ <bean id="FormOutboundMessage"
+ class="net.shibboleth.idp.plugin.oidc.op.oidfed.profile.impl.FormExplicitRegistrationResponse"
+ scope="prototype" />
+
+ <bean id="PostResponsePopulateAuditContext" parent="shibboleth.AbstractPopulateAuditContext"
+ p:fieldExtractors="#{getObject('shibboleth.oidc.RegistrationPostResponseAuditExtractors') ?: getObject('shibboleth.oidc.DefaultRegistrationPostResponseAuditExtractors')}" />
+
+</beans>
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidfed/register/register-flow.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidfed/register/register-flow.xml
new file mode 100644
index 00000000..493fae0c
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidfed/register/register-flow.xml
@@ -0,0 +1,49 @@
+<flow xmlns="http://www.springframework.org/schema/webflow"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow.xsd"
+ parent="oidc/abstract-register">
+
+ <action-state id="InitializeMandatoryContexts">
+ <evaluate expression="'proceed'" />
+ <transition on="proceed" to="DecodeMessage">
+ <set name="flowScope.transitionAfterDecode" value="'ResolveTrustChains'" />
+ </transition>
+ </action-state>
+
+ <action-state id="ResolveTrustChains">
+ <evaluate expression="ResolveTrustChains" />
+ <evaluate expression="'proceed'" />
+ <transition on="proceed" to="SelectTrustChain" />
+ </action-state>
+
+ <action-state id="SelectTrustChain">
+ <evaluate expression="SelectTrustChain" />
+ <evaluate expression="ResolveTrustMarks" />
+ <evaluate expression="SelectExplicitRegistrationRelyingPartyConfiguration" />
+ <evaluate expression="SelectExplicitRegistrationProfileConfiguration" />
+ <evaluate expression="ValidateExplicitRegistrationProfileConfiguration" />
+ <evaluate expression="PopulateInboundInterceptContext" />
+ <evaluate expression="'proceed'" />
+ <transition on="ReselectTrustChain" to="SelectTrustChain" />
+ <transition on="proceed" to="CheckInboundInterceptContext" />
+ </action-state>
+
+ <action-state id="OutboundContextsAndSecurityParameters">
+ <evaluate expression="CheckRedirectURIs" />
+ <evaluate expression="'proceed'" />
+ <transition on="proceed" to="BuildResponse" />
+ </action-state>
+
+ <action-state id="BuildResponse">
+ <evaluate expression="PopulateEntityStatementSignatureSigningParameters" />
+ <evaluate expression="BuildEntityStatement" />
+ <evaluate expression="SignEntityStatement" />
+ <evaluate expression="'proceed'" />
+ <transition on="proceed" to="FormResponseMessage">
+ <set name="flowScope.transitionAfterOutboundIntercept" value="'StoreClientInformation'" />
+ </transition>
+ </action-state>
+
+ <bean-import resource="register-beans.xml" />
+
+</flow>
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 41448710..c467e4d3 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
@@ -117,6 +117,11 @@
p:mandatoryTrustMarks="%{idp.oidfed.automaticRegistration.mandatoryTrustMarks:}"
p:securityConfiguration-ref="shibboleth.oidc.federation.DefaultSecurityConfiguration" />
+ <bean id="OIDFED.ExplicitRegistration" parent="AbstractOIDCProfile" lazy-init="true"
+ class="net.shibboleth.idp.plugin.oidc.op.oidfed.config.DefaultOIDFederationExplicitRegistrationConfiguration"
+ p:mandatoryTrustMarks="%{idp.oidfed.explicitRegistration.mandatoryTrustMarks:}"
+ p:securityConfiguration-ref="shibboleth.oidc.federation.DefaultSecurityConfiguration" />
+
<!-- Metadata-driven variants. -->
<bean id="AbstractMDDrivenOIDCProfile" parent="AbstractMDDrivenProfile" abstract="true">
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
new file mode 100644
index 00000000..f607eec4
--- /dev/null
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/RegistrationFlowTest.java
@@ -0,0 +1,101 @@
+/*
+ * 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.flow.oidfed;
+
+import org.opensaml.storage.StorageRecord;
+import org.opensaml.storage.StorageService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.webflow.executor.FlowExecutionResult;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.nimbusds.openid.connect.sdk.federation.entities.EntityStatement;
+import com.nimbusds.openid.connect.sdk.rp.OIDCClientInformation;
+import com.nimbusds.openid.connect.sdk.rp.OIDCClientMetadata;
+
+import net.minidev.json.JSONObject;
+import net.minidev.json.parser.JSONParser;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.messaging.impl.ExplicitClientRegistrationResponse;
+import net.shibboleth.oidc.metadata.impl.BaseStorageServiceClientInformationComponent;
+
+/**
+ * Flow tests for the OpenID federation explicit registration flow.
+ */
+public class RegistrationFlowTest extends AbstractFederationFlowTest {
+
+ public static final String FLOW_ID = "oidfed/register";
+
+ @Autowired
+ @Qualifier("shibboleth.StorageService")
+ StorageService storageService;
+
+ public RegistrationFlowTest() {
+ super(FLOW_ID);
+ }
+
+ @Test
+ public void testInvalidContentType() throws Exception {
+ setJsonRequest("POST", "{}");
+ final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+ assertErrorCode(result, "invalid_client_metadata");
+ }
+
+ @Test
+ public void testEmptyEntityConfiguration() throws Exception {
+ setRequest("POST", "", "application/entity-statement+jwt");
+ final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+ assertErrorCode(result, "invalid_client_metadata");
+ }
+
+ @Test
+ public void testValidEntityConfiguration_invalidType() throws Exception {
+ final String clientId = uniqueClientId();
+ configureMockHttpClient(clientId);
+ setRequest("POST", rpEntityConfiguration(clientId), "application/json");
+ final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+ assertErrorCode(result, "invalid_client_metadata");
+ }
+
+ @Test
+ public void testValidEntityConfiguration() throws Exception {
+ final String clientId = uniqueClientId();
+ 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 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.assertEquals(storedInfo.getOIDCMetadata().getRequestObjectURIs(), Set.of(new URI(requestUri)));
+ Assert.assertTrue(metadata.getRedirectionURIStrings().contains(redirectUri));
+
+ }
+}
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/BuildClientInformationTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/BuildClientInformationTest.java
index 21603ff3..efa610f6 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/BuildClientInformationTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/BuildClientInformationTest.java
@@ -28,7 +28,6 @@ import com.nimbusds.oauth2.sdk.auth.ClientAuthenticationMethod;
import com.nimbusds.oauth2.sdk.auth.Secret;
import com.nimbusds.oauth2.sdk.id.ClientID;
import com.nimbusds.openid.connect.sdk.rp.OIDCClientInformation;
-import com.nimbusds.openid.connect.sdk.rp.OIDCClientInformationResponse;
import com.nimbusds.openid.connect.sdk.rp.OIDCClientMetadata;
import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCClientRegistrationResponseContext;
@@ -71,13 +70,6 @@ public class BuildClientInformationTest {
registrationCtx.setClientMetadata(metadata);
}
- @Test
- public void noOutboundMessageContext() {
- final ProfileRequestContext localPrc = new ProfileRequestContext();
- action.execute(localPrc);
- ActionTestingSupport.assertEvent(localPrc, EventIds.INVALID_PROFILE_CTX);
- }
-
@Test
public void noMetadataContext() {
final ProfileRequestContext localPrc = new ProfileRequestContext();
@@ -168,19 +160,16 @@ public class BuildClientInformationTest {
protected void assertSuccessfulResponse(boolean secret) {
assert profileRequestCtx != null;
ActionTestingSupport.assertProceedEvent(profileRequestCtx);
- final OIDCClientInformationResponse response = (OIDCClientInformationResponse) messageCtx.getMessage();
- Assert.assertNotNull(response);
- assert response != null;
- final OIDCClientInformation clientInformation = response.getOIDCClientInformation();
+ final OIDCClientInformation clientInformation = registrationCtx.getClientInformation();
assert clientInformation != null;
Assert.assertEquals(clientInformation.getID(), new ClientID(clientId));
if (secret) {
- assertSecret(response);
+ assertSecret(clientInformation);
}
}
- protected void assertSecret(final OIDCClientInformationResponse response) {
- final Secret secret = response.getOIDCClientInformation().getSecret();
+ protected void assertSecret(final OIDCClientInformation information) {
+ final Secret secret = information.getSecret();
Assert.assertNotNull(secret);
Assert.assertEquals(secret.getValue(), clientSecret);
Assert.assertNull(secret.getExpirationDate());
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/CheckRedirectUrisTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/CheckRedirectUrisTest.java
index 0a292b87..b2f47061 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/CheckRedirectUrisTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/CheckRedirectUrisTest.java
@@ -67,7 +67,7 @@ public class CheckRedirectUrisTest extends BaseOIDCRegistrationRequestTest {
@Test
public void testNoMessage() throws ComponentInitializationException {
setUpContext(null);
- ActionTestingSupport.assertEvent(action.execute(requestCtx), EventIds.INVALID_MSG_CTX);
+ ActionTestingSupport.assertEvent(action.execute(requestCtx), EventIds.INVALID_MESSAGE);
}
@Test
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 83bc574e..0d2ac430 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
@@ -81,6 +81,7 @@
<property name="profileConfigurations">
<list>
<bean parent="OIDFED.AutomaticRegistration" p:mandatoryTrustMarks=""/>
+ <bean parent="OIDFED.ExplicitRegistration" p:mandatoryTrustMarks=""/>
</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