[java-idp-plugin-oidc-rp] branch main updated: First attempt to integrate the new JWT Trust engine for sig validation
Phil Smart
philip.smart at jisc.ac.uk
Fri Apr 29 16:12:32 UTC 2022
This is an automated email from the git hooks/post-receive script.
philsmart pushed a commit to branch main
in repository java-idp-plugin-oidc-rp.
View the commit online:
http://git.shibboleth.net/view/?p=java-idp-plugin-oidc-rp.git;a=commit;h=5b2c7709d7a17258655523115e093d7f859243f0
The following commit(s) were added to refs/heads/main by this push:
new 5b2c770 First attempt to integrate the new JWT Trust engine for sig validation
5b2c770 is described below
commit 5b2c7709d7a17258655523115e093d7f859243f0
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Fri Apr 29 17:12:26 2022 +0100
First attempt to integrate the new JWT Trust engine for sig validation
Most the heavy lifting is in oidc-common
Will break tests until synchronised with oidc-common
---
idp-oidc-rp-api/pom.xml | 3 +-
...atureValidationConfigurationLookupFunction.java | 92 +++
.../oidc/rp/context/AbstractOIDCEntityContext.java | 38 ++
.../oidc/rp/context/OIDCPeerEntityContext.java | 29 +-
.../oidc/rp/impl/AuthorizationController.java | 2 +-
...nitializeOAuth2ClientAuthenticationContext.java | 6 +-
.../authn/oidc/rp/impl/MockCredentialResolver.java | 35 ++
.../rp/impl/PrepareOIDCInboundMessageContext.java | 57 +-
.../oidc/rp/impl/ValidateIDTokenSignature.java | 108 ----
.../impl/OIDCProviderMetadataLookupHandler.java | 132 ++++-
.../META-INF/net.shibboleth.idp/postconfig.xml | 33 ++
.../oidc-relying-party-authn-beans.xml | 657 +++++++++++----------
.../oidc-relying-party-authn-flow.xml | 9 +-
.../idp/service/relying-party/postconfig.xml | 42 +-
.../plugin/authn/oidc/rp/impl/OIDCRPFlowTest.java | 51 +-
.../resources/conf/test-relying-party-system.xml | 8 +-
pom.xml | 6 +-
17 files changed, 808 insertions(+), 500 deletions(-)
diff --git a/idp-oidc-rp-api/pom.xml b/idp-oidc-rp-api/pom.xml
index ace1fa8..dd176a6 100644
--- a/idp-oidc-rp-api/pom.xml
+++ b/idp-oidc-rp-api/pom.xml
@@ -20,12 +20,13 @@
</properties>
<dependencies>
+
<!-- Provided dependencies -->
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<scope>provided</scope>
- </dependency>
+ </dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
diff --git a/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/navigate/IDTokenSignatureValidationConfigurationLookupFunction.java b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/navigate/IDTokenSignatureValidationConfigurationLookupFunction.java
new file mode 100644
index 0000000..c4856ff
--- /dev/null
+++ b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/navigate/IDTokenSignatureValidationConfigurationLookupFunction.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements. See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You 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.authn.oidc.rp.config.navigate;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.idp.profile.config.ProfileConfiguration;
+import net.shibboleth.idp.profile.config.SecurityConfiguration;
+import net.shibboleth.idp.profile.context.RelyingPartyContext;
+import net.shibboleth.idp.profile.context.navigate.AbstractRelyingPartyLookupFunction;
+import net.shibboleth.idp.relyingparty.RelyingPartyConfigurationResolver;
+import net.shibboleth.oidc.profile.config.OIDCSecurityConfiguration;
+import net.shibboleth.oidc.security.SignatureValidationConfiguration;
+
+/**
+ * A function that returns a {@link SignatureValidationConfiguration} list for id_token signature validation by way
+ * of various lookup strategies.
+ *
+ * <p>
+ * If a specific setting is unavailable, a null value is returned.
+ * </p>
+ */
+public class IDTokenSignatureValidationConfigurationLookupFunction
+ extends AbstractRelyingPartyLookupFunction<List<SignatureValidationConfiguration>> {
+
+ /** A resolver for default security configurations. */
+ @Nullable
+ private RelyingPartyConfigurationResolver rpResolver;
+
+ /**
+ * Set the resolver for default security configurations.
+ *
+ * @param resolver the resolver to use
+ */
+ public void setRelyingPartyConfigurationResolver(@Nullable final RelyingPartyConfigurationResolver resolver) {
+ rpResolver = resolver;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ @Nullable
+ public List<SignatureValidationConfiguration> apply(@Nullable final ProfileRequestContext input) {
+
+ final List<SignatureValidationConfiguration> configs = new ArrayList<>();
+
+ final RelyingPartyContext rpc = getRelyingPartyContextLookupStrategy().apply(input);
+ if (rpc != null) {
+ final ProfileConfiguration pc = rpc.getProfileConfig();
+ if (pc != null && pc.getSecurityConfiguration(input) instanceof OIDCSecurityConfiguration
+ && ((OIDCSecurityConfiguration) pc.getSecurityConfiguration(input))
+ .getIdTokenJwtSignatureValidationConfig() != null) {
+ configs.add(((OIDCSecurityConfiguration) pc.getSecurityConfiguration(input))
+ .getIdTokenJwtSignatureValidationConfig());
+ }
+ }
+
+ // Check for a per-profile default (relying party independent) config.
+ if (input != null && rpResolver != null) {
+ final SecurityConfiguration defaultConfig =
+ rpResolver.getDefaultSecurityConfiguration(input.getProfileId());
+ if (defaultConfig instanceof OIDCSecurityConfiguration
+ && ((OIDCSecurityConfiguration) defaultConfig)
+ .getIdTokenJwtSignatureValidationConfig() != null) {
+ configs.add(
+ ((OIDCSecurityConfiguration) defaultConfig).getIdTokenJwtSignatureValidationConfig());
+ }
+ }
+ // TODO: Support for Global Default configuration?
+ return configs;
+ }
+}
+
diff --git a/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/context/AbstractOIDCEntityContext.java b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/context/AbstractOIDCEntityContext.java
new file mode 100644
index 0000000..418d661
--- /dev/null
+++ b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/context/AbstractOIDCEntityContext.java
@@ -0,0 +1,38 @@
+package net.shibboleth.idp.plugin.authn.oidc.rp.context;
+
+import javax.annotation.Nullable;
+
+import org.opensaml.messaging.context.BaseContext;
+
+import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
+import net.shibboleth.utilities.java.support.primitive.StringSupport;
+
+
+/**
+ * Abstract base class for subcontexts that carry information about a OIDC entity. This context will often
+ * contain subcontexts, whose data is construed to be scoped to that entity.
+ */
+public class AbstractOIDCEntityContext extends BaseContext {
+
+ /** The identifier of the OIDC peer entity e.g. issuerId or ClientId. */
+ @Nullable @NotEmpty private String identifer;
+
+ /**
+ * Gets the identifier of the OIDC entity.
+ *
+ * @return identifier of the OIDC entity, may be null
+ */
+ @Nullable @NotEmpty public String getIdentifier() {
+ return identifer;
+ }
+
+ /**
+ * Sets the identifier of the OIDC entity e.g. issuerId or ClientId.
+ *
+ * @param id the new identifier
+ */
+ public void setIdentifier(@Nullable final String id) {
+ identifer = StringSupport.trimOrNull(id);
+ }
+
+}
diff --git a/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/context/OIDCPeerEntityContext.java b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/context/OIDCPeerEntityContext.java
index 86dcc70..97bb68b 100644
--- a/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/context/OIDCPeerEntityContext.java
+++ b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/context/OIDCPeerEntityContext.java
@@ -17,13 +17,6 @@
package net.shibboleth.idp.plugin.authn.oidc.rp.context;
-import javax.annotation.Nullable;
-
-import org.opensaml.messaging.context.BaseContext;
-
-import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
-import net.shibboleth.utilities.java.support.primitive.StringSupport;
-
/**
* Lightweight subcontext that carries information about a OIDC peer entity.
*
@@ -31,27 +24,9 @@ import net.shibboleth.utilities.java.support.primitive.StringSupport;
* This context will often contain subcontexts, whose data is construed to be scoped to that peer entity.
* </p>
*/
-public final class OIDCPeerEntityContext extends BaseContext {
+//TODO this is just a marker interface?
+public final class OIDCPeerEntityContext extends AbstractOIDCEntityContext {
- /** The identifier of the OIDC peer entity e.g. issuerId or ClientId. */
- @Nullable @NotEmpty private String identifer;
- /**
- * Gets the identifier of the OIDC entity.
- *
- * @return identifier of the OIDC entity, may be null
- */
- @Nullable @NotEmpty public String getIdentifier() {
- return identifer;
- }
-
- /**
- * Sets the identifier of the OIDC entity e.g. issuerId or ClientId.
- *
- * @param id the new identifier
- */
- public void setIdentifier(@Nullable final String id) {
- identifer = StringSupport.trimOrNull(id);
- }
}
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AuthorizationController.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AuthorizationController.java
index 15a609c..456afc8 100644
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AuthorizationController.java
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AuthorizationController.java
@@ -332,7 +332,7 @@ public class AuthorizationController extends AbstractInitializableComponent {
return;
}
log.debug("OIDC response_type '{}' and response_mode '{}' requested, decoding incoming request",
- responseCtx.getResponseMode(), responseCtx.getResponseType());
+ responseCtx.getResponseType(), responseCtx.getResponseMode());
try {
final MessageDecoder decoder =
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/InitializeOAuth2ClientAuthenticationContext.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/InitializeOAuth2ClientAuthenticationContext.java
index a2e4867..e9d564a 100644
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/InitializeOAuth2ClientAuthenticationContext.java
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/InitializeOAuth2ClientAuthenticationContext.java
@@ -53,10 +53,10 @@ public class InitializeOAuth2ClientAuthenticationContext extends AbstractProfile
@Nonnull
private final Logger log = LoggerFactory.getLogger(InitializeOAuth2ClientAuthenticationContext.class);
-
/**
* The strategy used to lookup or create the {@link OAuth2ClientAuthenticationContext}
- * for storing the client authentication.*/
+ * for storing the client authentication.
+ */
@Nonnull private Function<ProfileRequestContext, OAuth2ClientAuthenticationContext>
oauth2ClientAuthenticationContextLookupStrategy;
@@ -136,7 +136,7 @@ public class InitializeOAuth2ClientAuthenticationContext extends AbstractProfile
profileConfiguration = (OIDCAuthorizationConfiguration) rpCtx.getProfileConfig();
}
if (profileConfiguration == null) {
- log.error("{} OIDCAuthorizationConfiguration not found", getLogPrefix());
+ log.error("{} Profile configuration not found", getLogPrefix());
ActionSupport.buildEvent(profileRequestContext, IdPEventIds.INVALID_PROFILE_CONFIG);
return false;
}
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/MockCredentialResolver.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/MockCredentialResolver.java
new file mode 100644
index 0000000..7d9f43c
--- /dev/null
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/MockCredentialResolver.java
@@ -0,0 +1,35 @@
+package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
+
+import java.util.List;
+
+import javax.crypto.spec.SecretKeySpec;
+
+import org.opensaml.security.credential.Credential;
+import org.opensaml.security.credential.CredentialResolver;
+
+import com.nimbusds.jose.Algorithm;
+
+import net.shibboleth.oidc.security.credential.BasicJWKCredential;
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+import net.shibboleth.utilities.java.support.resolver.ResolverException;
+
+// TODO tmp class to fit into the trust engine.
+public class MockCredentialResolver implements CredentialResolver {
+
+ private static final String ID_TOKEN_HMAC_SECRET = "Xp2s5v8y/B?E(H+MbQeThWmYq3t6w9z$";
+
+ @Override
+ public Iterable<Credential> resolve(final CriteriaSet criteria) throws ResolverException {
+ return List.of(resolveSingle(criteria));
+ }
+
+ @Override
+ public Credential resolveSingle(final CriteriaSet criteria) throws ResolverException {
+ final var cred = new BasicJWKCredential();
+ cred.setAlgorithm(Algorithm.parse("HS256"));
+ cred.setSecretKey(new SecretKeySpec(ID_TOKEN_HMAC_SECRET.getBytes(), "HS256"));
+ return cred;
+
+ }
+
+}
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/PrepareOIDCInboundMessageContext.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/PrepareOIDCInboundMessageContext.java
index 9c45c99..509273a 100644
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/PrepareOIDCInboundMessageContext.java
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/PrepareOIDCInboundMessageContext.java
@@ -18,6 +18,7 @@
package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
import java.util.function.Function;
+import java.util.function.Predicate;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -29,6 +30,8 @@ import org.opensaml.profile.context.ProfileRequestContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.base.Predicates;
+
import net.shibboleth.idp.authn.context.AuthenticationContext;
import net.shibboleth.idp.plugin.authn.oidc.rp.context.OIDCPeerEntityContext;
import net.shibboleth.idp.profile.AbstractProfileAction;
@@ -42,6 +45,9 @@ import net.shibboleth.utilities.java.support.logic.Constraint;
* {@link ProfileRequestContext} based on the identity of a relying party, by default from the
* {@link AuthenticationContext#getAuthenticatingAuthority()}.
*
+ * <p>If {@link #addToExistingInboundMessageContext} is true, the {@link OIDCPeerEntityContext} is
+ * added as a subcontext to any existing inbound message context.</p>
+ *
* @event {@link EventIds#PROCEED_EVENT_ID}
* @event {@link EventIds#INVALID_PROFILE_CTX}
*/
@@ -56,6 +62,18 @@ public class PrepareOIDCInboundMessageContext extends AbstractProfileAction {
/** The identifier of the OP/RP to base the inbound context on. */
@Nullable private String identifier;
+ /**
+ * Should the peer entity context be added to an existing inbound message context or not.
+ * If not, a new inbound message context is created before the peer entity context is added.
+ * Defaults to false.
+ */
+ @Nonnull private Predicate<ProfileRequestContext> addToExistingInboundMessageContextPredicate;
+
+ /** Constructor.*/
+ public PrepareOIDCInboundMessageContext() {
+ addToExistingInboundMessageContextPredicate = Predicates.alwaysFalse();
+ }
+
@Override
protected void doInitialize() throws ComponentInitializationException {
super.doInitialize();
@@ -65,6 +83,30 @@ public class PrepareOIDCInboundMessageContext extends AbstractProfileAction {
}
}
+ /**
+ * Set whether to append any defined subcontexts to the existing inbound message context, or to a new one.
+ *
+ * @param flag the flag to set.
+ */
+ public void setAddToExistingInboundMessageContext(final boolean flag) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+ addToExistingInboundMessageContextPredicate = flag ? Predicates.alwaysTrue() : Predicates.alwaysFalse();
+ }
+
+ /**
+ * Set a predicate to determine whether to append any defined subcontexts to the existing inbound
+ * message context, or to a new one.
+ *
+ * @param predicate the predicate to set.
+ */
+ public void setAddToExistingInboundMessageContextPredicate(
+ @Nonnull final Predicate<ProfileRequestContext> predicate) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+ addToExistingInboundMessageContextPredicate = Constraint.isNotNull(predicate, "The predicate can not be null");
+ }
+
/**
* Set the lookup strategy to identify the OP/RP.
*
@@ -93,8 +135,19 @@ public class PrepareOIDCInboundMessageContext extends AbstractProfileAction {
@Override protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
- final MessageContext msgCtx = new MessageContext();
- profileRequestContext.setInboundMessageContext(msgCtx);
+ MessageContext msgCtx = null;
+ if (addToExistingInboundMessageContextPredicate.test(profileRequestContext)) {
+ msgCtx = profileRequestContext.getInboundMessageContext();
+ if (msgCtx == null) {
+ log.warn("{} Profile request context did not contain an inbound message context", getLogPrefix());
+ ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
+ return;
+ }
+ } else {
+ msgCtx = new MessageContext();
+ profileRequestContext.setInboundMessageContext(msgCtx);
+ }
+
final OIDCPeerEntityContext peerContext = msgCtx.getSubcontext(OIDCPeerEntityContext.class, true);
peerContext.setIdentifier(identifier);
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ValidateIDTokenSignature.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ValidateIDTokenSignature.java
deleted file mode 100644
index 992a6eb..0000000
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ValidateIDTokenSignature.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements. See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID licenses this file to You 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.authn.oidc.rp.impl;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.StringWriter;
-import java.text.ParseException;
-import java.util.Map;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import org.opensaml.profile.context.ProfileRequestContext;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.base.Charsets;
-import com.google.common.io.CharStreams;
-import com.nimbusds.jose.shaded.json.JSONArray;
-import com.nimbusds.jose.shaded.json.JSONObject;
-import com.nimbusds.jose.util.JSONObjectUtils;
-
-import net.shibboleth.idp.authn.AbstractAuthenticationAction;
-import net.shibboleth.idp.authn.AuthnEventIds;
-import net.shibboleth.idp.authn.context.AuthenticationContext;
-
-/**
- * An action that verifies the signature of a JWS id_token using the RSA key belonging to the keyID
- * found in the JOSE Header.
- *
- * <p>The current implementation *requires* the id_token is signed, and *requires* an RSA key type</p>
- *
- * @event {@link org.opensaml.profile.action.EventIds#PROCEED_EVENT_ID}
- * @event {@link AuthnEventIds#NO_CREDENTIALS}
- * @pre <pre>ProfileRequestContext.getSubcontext(AuthenticationContext.class, false) != null</pre>
- * @pre <pre>AuthenticationContext.getSubcontext(OpenIDConnectContext.class, false) != null</pre>
- * @pre <pre>OpenIdConnectContext.getoIDCProviderMetadata() != null</pre>
- *
- * @since 4.0.0
- */
-public class ValidateIDTokenSignature extends AbstractAuthenticationAction {
-
- /** Class logger. */
- @Nonnull private final Logger log = LoggerFactory.getLogger(ValidateIDTokenSignature.class);
-
- /** {@inheritDoc} */
- @Override
- protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
- @Nonnull final AuthenticationContext authenticationContext) {
-
-
- }
-
- /**
- * Parse JWK and RSA public key from the input stream for signature verification.
- *
- * @param is inputstream containing the JWK
- * @param kid The key ID to be looked up
- * @return RSA public key as a JSON Object. <code>Null</code> if there is no key
- * @throws ParseException if parsing fails.
- * @throws IOException if something unexpected happens.
- */
- //TODO could be cached? (or use Nimbus to do this validation as it does cache it).
- @Nullable
- private JSONObject getProviderRSAJWK(@Nonnull final InputStream is, @Nullable final String kid)
- throws ParseException, IOException {
-
- if (kid == null) {
- log.warn("No kid defined in the JWT, no signning key can be returned");
- }
-
- final StringWriter writer = new StringWriter();
- CharStreams.copy(new InputStreamReader(is, Charsets.UTF_8), writer);
-
- final Map<String,Object> json = JSONObjectUtils.parse(writer.toString());
- final JSONArray keyList = (JSONArray) json.get("keys");
- if (keyList == null) {
- return null;
- }
- for (final Object key : keyList) {
- final JSONObject k = (JSONObject) key;
- if ("sig".equals(k.get("use")) && "RSA".equals(k.get("kty"))) {
- if (kid == null || kid.equals(k.get("kid"))) {
- return k;
- }
- }
- }
- return null;
- }
-
-}
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/metadata/impl/OIDCProviderMetadataLookupHandler.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/metadata/impl/OIDCProviderMetadataLookupHandler.java
index 4939173..3ecae14 100644
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/metadata/impl/OIDCProviderMetadataLookupHandler.java
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/metadata/impl/OIDCProviderMetadataLookupHandler.java
@@ -1,20 +1,41 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements. See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You 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.authn.oidc.rp.metadata.impl;
+import java.util.Objects;
import java.util.function.Function;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
-import org.opensaml.messaging.context.BaseContext;
import org.opensaml.messaging.context.MessageContext;
import org.opensaml.messaging.context.navigate.ChildContextLookup;
import org.opensaml.messaging.handler.AbstractMessageHandler;
import org.opensaml.messaging.handler.MessageHandlerException;
+import org.opensaml.saml.common.messaging.context.SAMLMetadataContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.nimbusds.oauth2.sdk.id.Issuer;
import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
+import net.shibboleth.idp.plugin.authn.oidc.rp.context.AbstractOIDCEntityContext;
import net.shibboleth.idp.plugin.authn.oidc.rp.context.OIDCPeerEntityContext;
import net.shibboleth.oidc.metadata.ProviderMetadataResolver;
import net.shibboleth.oidc.metadata.context.OIDCMetadataContext;
@@ -29,8 +50,14 @@ import net.shibboleth.utilities.java.support.resolver.ResolverException;
/**
- * Handler for inbound OIDC protocol messages that attempts to locate OIDC metadata for a OP (issuer), and attaches it with a
- * {@link OIDCMetadataContext} as a child of a pre-existing instance of {@link MessageContext}.
+ * Handler for inbound OIDC protocol messages that attempts to locate OIDC metadata for a OP (issuer),
+ * and attaches it with a {@link OIDCMetadataContext} as a child of a pre-existing instance of {@link MessageContext}.
+ *
+ * <p>
+ * If the optional copy strategy is configured via {@link #setCopyContextStrategy(Function)},
+ * and if that lookup finds an existing metadata context with compatible data (matching the IssuerID),
+ * then its data will be re-used.
+ * </p>
*/
//TODO This might need a way to set which base class to add the metadata too.
public class OIDCProviderMetadataLookupHandler extends AbstractMessageHandler {
@@ -40,20 +67,24 @@ public class OIDCProviderMetadataLookupHandler extends AbstractMessageHandler {
/** Resolver used to look up OIDC provider information. */
@NonnullAfterInit private ProviderMetadataResolver providerResolver;
-
- /** Strategy used to obtain the issuer id value for the inbound message context. */
- @NonnullAfterInit private Function<MessageContext,String> issuerIDLookupStrategy;
-
+
/** Strategy to resolve the context class to add the resolved metadata too.*/
- @Nonnull private Function<MessageContext,? extends BaseContext> contextClassLookupStrategy;
+ @Nonnull private Function<MessageContext,? extends AbstractOIDCEntityContext> contextClassLookupStrategy;
+
+ /** Optional strategy for resolving an existing metadata context from which to copy data. */
+ @Nullable private Function<MessageContext, OIDCProviderMetadataContext> copyContextStrategy;
/** Constructor.*/
public OIDCProviderMetadataLookupHandler() {
contextClassLookupStrategy = new ChildContextLookup<>(OIDCPeerEntityContext.class);
}
+ /** Set the context class lookup strategy.
+ *
+ * @param strategy the strategy.
+ */
public void setContextClassLookupStrategy(
- @Nonnull final Function<MessageContext, ? extends BaseContext> strategy) {
+ @Nonnull final Function<MessageContext, ? extends AbstractOIDCEntityContext> strategy) {
ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
contextClassLookupStrategy =
@@ -61,15 +92,14 @@ public class OIDCProviderMetadataLookupHandler extends AbstractMessageHandler {
}
/**
- * Set the strategy used to locate the client id of the request.
- *
- * @param strategy lookup strategy
+ * Set the optional strategy for resolving an existing metadata context from which to copy data.
+ *
+ * @param strategy the strategy function
*/
- public void setIssuerIDLookupStrategy(@Nonnull final Function<MessageContext, String> strategy) {
+ public void setCopyContextStrategy(@Nullable final Function<MessageContext, OIDCProviderMetadataContext> strategy) {
ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
- ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
- issuerIDLookupStrategy =
- Constraint.isNotNull(strategy, "IssuerIDLookupStrategy lookup strategy cannot be null");
+
+ copyContextStrategy = strategy;
}
@@ -93,38 +123,38 @@ public class OIDCProviderMetadataLookupHandler extends AbstractMessageHandler {
if (providerResolver == null) {
throw new ComponentInitializationException("IssuerMetadataResolver cannot be null");
}
- if (issuerIDLookupStrategy == null) {
- throw new ComponentInitializationException("IssuerIDLookupStrategy cannot be null");
- }
+
}
@Override
protected void doInvoke(final MessageContext messageContext) throws MessageHandlerException {
ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
- final BaseContext entityCtx = contextClassLookupStrategy.apply(messageContext);
+ final AbstractOIDCEntityContext entityCtx = contextClassLookupStrategy.apply(messageContext);
- if (entityCtx == null) {
- log.info("{} OIDC entity context class '{}' missing", getLogPrefix(),
- OIDCPeerEntityContext.class);
+ if (entityCtx == null || entityCtx.getIdentifier() == null) {
+ log.info("{} OIDC entity context class '{}' missing or did not contain an issuer identifier",
+ getLogPrefix(), AbstractOIDCEntityContext.class);
return;
}
- // Resolve issuer id from inbound message
- final String issuerId = issuerIDLookupStrategy.apply(messageContext);
- if (issuerId == null) {
- log.warn("{} No issuer returned from lookup strategy", getLogPrefix());
+ final OIDCProviderMetadataContext existingMetadataCtx = resolveExisting(messageContext,
+ entityCtx.getIdentifier());
+ if (existingMetadataCtx != null) {
+ log.info("{} Resolved existing provider metadata context, re-using it", getLogPrefix());
+ entityCtx.addSubcontext(existingMetadataCtx);
return;
}
- final IssuerIDCriterion issuerCriterion = new IssuerIDCriterion(new Issuer(issuerId));
+
+ final IssuerIDCriterion issuerCriterion = new IssuerIDCriterion(new Issuer(entityCtx.getIdentifier()));
final CriteriaSet criteria = new CriteriaSet(issuerCriterion);
try {
final OIDCProviderMetadata issuerMetadata = providerResolver.resolveSingle(criteria);
if (issuerMetadata == null) {
- log.debug("{} No provider metadata returned for {}",getLogPrefix(), issuerId);
+ log.debug("{} No provider metadata returned for {}",getLogPrefix(), entityCtx.getIdentifier());
return;
}
- log.debug("{} Found provider metadata for '{}'", getLogPrefix(), issuerId);
+ log.debug("{} Found provider metadata for '{}'", getLogPrefix(), entityCtx.getIdentifier());
final OIDCProviderMetadataContext context = new OIDCProviderMetadataContext();
context.setProviderInformation(issuerMetadata);
entityCtx.addSubcontext(context);
@@ -132,5 +162,47 @@ public class OIDCProviderMetadataLookupHandler extends AbstractMessageHandler {
log.error("{} ResolverException thrown during provider metadata lookup", getLogPrefix(), e);
}
}
+
+ /**
+ * Attempt to resolve an existing {@link OIDCProviderMetadataContext} from which to copy.
+ *
+ * <p>
+ * The returned context will always be a fresh parent-less instance, suitable for the caller to
+ * directly store in the current message context.
+ * </p>
+ *
+ * @param messageContext the current message context
+ * @param issuer the identifier of the issuer against which to match
+ *
+ * @return a new instance of {@link SAMLMetadataContext}, or null if one can not be resolved
+ */
+ @Nullable protected OIDCProviderMetadataContext resolveExisting(@Nonnull final MessageContext messageContext,
+ @Nonnull final String issuer) {
+
+ if (copyContextStrategy == null) {
+ return null;
+ }
+
+ final OIDCProviderMetadataContext existing = copyContextStrategy.apply(messageContext);
+ if (existing != null) {
+ if (existing.getProviderInformation() != null) {
+ // Validate that existing data has the same issuer
+ if (Objects.equals(existing.getProviderInformation().getIssuer().getValue(), issuer)) {
+ log.debug("{} Found an existing and suitable OIDCProviderMetadataContext from which to copy ",
+ getLogPrefix());
+ final OIDCProviderMetadataContext copy = new OIDCProviderMetadataContext();
+ copy.setProviderInformation(existing.getProviderInformation());
+ return copy;
+ }
+ log.debug("{} Existing OIDCProviderMetadataContext was resolved, but the issuer "
+ + "did not match the entity context data", getLogPrefix());
+ }
+ log.debug("{} Existing OIDCProviderMetadataContext was resolved, but was missing ProviderInformation "
+ + "data", getLogPrefix());
+ } else {
+ log.debug("{} No existing OIDCProviderMetadataContext was resolved", getLogPrefix());
+ }
+ return null;
+ }
}
diff --git a/idp-oidc-rp-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml b/idp-oidc-rp-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
index 1727532..008cf5d 100644
--- a/idp-oidc-rp-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
+++ b/idp-oidc-rp-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
@@ -35,6 +35,33 @@
</constructor-arg>
</bean>
+ <bean id="shibboleth.ChildLookup.ProviderMetadataFromProviderContext"
+ class="net.shibboleth.idp.profile.context.navigate.SpringExpressionContextLookupFunction"
+ c:_0="#{ T(net.shibboleth.oidc.metadata.context.OIDCProviderMetadataContext) }"
+ c:outputType="#{T(com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata)}"
+ c:expression="#input.getProviderInformation()" />
+
+
+ <bean id="shibboleth.ChildLookup.OIDCProviderMetadataContextFromPeerEntityContext"
+ parent="shibboleth.Functions.Compose">
+ <constructor-arg name="g">
+ <ref bean="shibboleth.ChildLookup.OIDCProviderMetadataContext" />
+ </constructor-arg>
+ <constructor-arg name="f">
+ <ref bean="shibboleth.ChildLookup.OIDCPeerEntityContext" />
+ </constructor-arg>
+ </bean>
+
+ <bean id="shibboleth.ChildLookup.OIDCProviderMetadataFromPeerEntityContext"
+ parent="shibboleth.Functions.Compose">
+ <constructor-arg name="g">
+ <ref bean="shibboleth.ChildLookup.ProviderMetadataFromProviderContext" />
+ </constructor-arg>
+ <constructor-arg name="f">
+ <ref bean="shibboleth.ChildLookup.OIDCProviderMetadataContextFromPeerEntityContext" />
+ </constructor-arg>
+ </bean>
+
<bean id="shibboleth.ChildLookup.OAuth2ClientContextFromOutbound" parent="shibboleth.Functions.Compose">
<constructor-arg name="g">
<ref bean="shibboleth.ChildLookup.OAuth2ClientContext" />
@@ -148,6 +175,12 @@
</property>
</bean>
+ <!-- JWK Cache service for provider keys used inside the trust engine. Will use the same storage engine and context and
+ the OP?! The key is the URI, so that should be fine. -->
+ <bean id="shibboleth.authn.oidc.rp.RemoteJwkSetCache" class="net.shibboleth.oidc.jwk.RemoteJwkSetCache"
+ p:storage-ref="#{'%{idp.oidc.rp.jwk.StorageService:shibboleth.StorageService}'.trim()}"
+ p:httpClient="#{getObject('shibboleth.oidc.rp.NonBrowser.HttpClient') ?: getObject('shibboleth.InternalHttpClient')}"
+ p:httpClientSecurityParameters="#{getObject('shibboleth.oidc.rp.NonBrowser.HttpClientSecurityParameters')}" />
diff --git a/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-beans.xml b/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-beans.xml
index df7f839..4a34e36 100644
--- a/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-beans.xml
+++ b/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-beans.xml
@@ -1,31 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:context="http://www.springframework.org/schema/context"
- xmlns:util="http://www.springframework.org/schema/util"
- xmlns:p="http://www.springframework.org/schema/p"
- xmlns:c="http://www.springframework.org/schema/c"
- 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
+ xmlns:context="http://www.springframework.org/schema/context"
+ xmlns:util="http://www.springframework.org/schema/util" xmlns:p="http://www.springframework.org/schema/p"
+ xmlns:c="http://www.springframework.org/schema/c" 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 class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"
- p:placeholderPrefix="%{" p:placeholderSuffix="}" />
+
+ default-init-method="initialize" default-destroy-method="destroy">
+
+ <bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer" p:placeholderPrefix="%{"
+ p:placeholderSuffix="}" />
<bean class="net.shibboleth.ext.spring.config.IdentifiableBeanPostProcessor" />
<bean class="net.shibboleth.idp.profile.impl.ProfileActionBeanPostProcessor" />
-
-
+
+
<!-- Initial discovery step -->
<bean id="PropertyDrivenDiscovery" parent="shibboleth.Functions.Constant"
- c:target="#{'%{idp.authn.oidc.rp.proxyIssuer:}'.trim()}" />
-
+ c:target="#{'%{idp.authn.oidc.rp.proxyIssuer:}'.trim()}" />
+
<!-- Parent beans for indirecting into nested PRC. -->
-
+
<bean id="NestedWebFlowMessageHandlerAdaptor" abstract="true"
class="net.shibboleth.idp.profile.impl.WebFlowMessageHandlerAdaptor" scope="prototype"
p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext" />
@@ -33,91 +30,86 @@
<bean id="NestedWebFlowProfileActionAdaptor" abstract="true"
class="net.shibboleth.idp.profile.impl.WebFlowProfileActionAdaptor" scope="prototype"
p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext" />
-
- <bean id="ParentAuthenticiationContextLookup" class="org.opensaml.messaging.context.navigate.ParentContextLookup"
+
+ <bean id="ParentAuthenticiationContextLookup"
+ class="org.opensaml.messaging.context.navigate.ParentContextLookup"
c:type="net.shibboleth.idp.authn.context.AuthenticationContext" />
-
- <!-- Action beans -->
-
+
+ <!-- Action beans -->
+
<!-- Explicitly wrapped by a non-nested action adaptor. -->
-
- <!-- FIXME (add this back) p:profileId="#{T(net.shibboleth.idp.plugin.oidc.op.config.OIDCCoreProtocolConfiguration).PROFILE_ID}"-->
+
+ <!-- FIXME (add this back) p:profileId="#{T(net.shibboleth.idp.plugin.oidc.op.config.OIDCCoreProtocolConfiguration).PROFILE_ID}" -->
<bean id="InitializeProxyProfileRequestContext"
- class="net.shibboleth.idp.authn.proxy.impl.InitializeProxyProfileRequestContext"
+ class="net.shibboleth.idp.authn.proxy.impl.InitializeProxyProfileRequestContext"
p:profileId="http://shibboleth.net/ns/profiles/oidc/sso/browser"
- p:loggingId="%{idp.service.logging.oidcsso:OIDC.SSO}"
- p:browserProfile="true" />
-
- <bean id="FlowStartPopulateAuditContext" parent="shibboleth.AbstractPopulateAuditContext"
+ p:loggingId="%{idp.service.logging.oidcsso:OIDC.SSO}" p:browserProfile="true" />
+
+ <bean id="FlowStartPopulateAuditContext" parent="shibboleth.AbstractPopulateAuditContext"
p:fieldExtractors="#{getObject('shibboleth.FlowStartAuditExtractors') ?: getObject('shibboleth.DefaultFlowStartAuditExtractors')}" />
-
-
- <bean id="PrepareOIDCInboundMessageContext"
- class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.PrepareOIDCInboundMessageContext" scope="prototype"
- p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext">
+
+
+ <bean id="PrepareOIDCInboundMessageContext"
+ class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.PrepareOIDCInboundMessageContext" scope="prototype"
+ p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext">
<property name="identiferLookupStrategy">
- <bean parent="shibboleth.Functions.Compose" c:f-ref="ParentAuthenticiationContextLookup">
- <constructor-arg name="g">
- <bean parent="shibboleth.Functions.Expression" c:expression="#input.getAuthenticatingAuthority()" />
- </constructor-arg>
- </bean>
+ <bean parent="shibboleth.Functions.Compose" c:f-ref="ParentAuthenticiationContextLookup">
+ <constructor-arg name="g">
+ <bean parent="shibboleth.Functions.Expression"
+ c:expression="#input.getAuthenticatingAuthority()" />
+ </constructor-arg>
+ </bean>
</property>
</bean>
-
- <bean id="OIDCProviderMetadataLookup" parent="NestedWebFlowMessageHandlerAdaptor"
- scope="prototype" c:executionDirection="INBOUND">
+
+ <bean id="OIDCProviderMetadataLookup" parent="NestedWebFlowMessageHandlerAdaptor" scope="prototype"
+ c:executionDirection="INBOUND">
<constructor-arg name="messageHandler">
- <bean class="net.shibboleth.idp.plugin.authn.oidc.rp.metadata.impl.OIDCProviderMetadataLookupHandler"
- scope="prototype">
- <property name="providerMetadataResolver">
+ <bean class="net.shibboleth.idp.plugin.authn.oidc.rp.metadata.impl.OIDCProviderMetadataLookupHandler"
+ scope="prototype">
+ <property name="providerMetadataResolver">
<ref bean="shibboleth.oidc.rp.ProviderMetadataResolver" />
</property>
- <property name="issuerIDLookupStrategy">
- <ref bean="shibboleth.oidc.rp.IssuerIDLookupStrategy" />
- </property>
</bean>
</constructor-arg>
- </bean>
+ </bean>
+
-
<bean id="shibboleth.oidc.rp.IssuerIDLookupStrategy"
- class="net.shibboleth.idp.plugin.authn.oidc.rp.metadata.impl.DefaultIssuerIDLookupFunction"
- scope="prototype" />
+ class="net.shibboleth.idp.plugin.authn.oidc.rp.metadata.impl.DefaultIssuerIDLookupFunction" scope="prototype" />
<bean id="InitializeRelyingPartyContext"
class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.InitializeRelyingPartyContext" scope="prototype"
p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext" />
-
-
+
+
<bean id="InitializeOutboundMessageContext"
class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.InitializeOutboundAuthorizationRequestMessageContext"
- p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext"
- scope="prototype" />
-
- <bean id="InitializeOAuth2ClientContext" scope="prototype"
+ p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext" scope="prototype" />
+
+ <bean id="InitializeOAuth2ClientContext" scope="prototype"
class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.InitializeOAuth2ClientContext"
- p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext"/>
-
-
+ p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext" />
+
+
<bean id="SelectRelyingPartyConfiguration"
class="net.shibboleth.idp.profile.impl.SelectRelyingPartyConfiguration" scope="prototype"
p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext"
- p:relyingPartyConfigurationResolver-ref="shibboleth.RelyingPartyConfigurationResolver" />
-
- <bean id="SelectProfileConfiguration"
- class="net.shibboleth.idp.profile.impl.SelectProfileConfiguration" scope="prototype"
- p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext" />
-
+ p:relyingPartyConfigurationResolver-ref="shibboleth.RelyingPartyConfigurationResolver" />
+
+ <bean id="SelectProfileConfiguration" class="net.shibboleth.idp.profile.impl.SelectProfileConfiguration"
+ scope="prototype" p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext" />
+
<bean id="PopulateResponseTypeAndModeContext" scope="prototype"
class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.PopulateResponseTypeAndModeContext"
- p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext"/>
-
+ p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext" />
+
<bean id="AddOIDCAuthenticationRequest" scope="prototype"
class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.AddOIDCAuthenticationRequest"
- p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext"/>
-
-
- <!-- Message Encoder factory is a prototype to allow reuse of the encoders -->
+ p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext" />
+
+
+ <!-- Message Encoder factory is a prototype to allow reuse of the encoders -->
<bean id="messageEncoderFactory"
class="net.shibboleth.oidc.profile.impl.AuthenticationRequestMessageEncoderFactory" scope="prototype"
c:encoders-ref="shibboleth.authn.oidc.rp.AuthenticationRequestEncoders" />
@@ -129,26 +121,27 @@
</util:list>
<bean id="HTTPRedirectAuthnRequestEncoder"
- class="net.shibboleth.oidc.profile.encoder.impl.HTTPRedirectAuthnRequestEncoder" init-method="" scope="prototype"
- p:httpServletResponse-ref="shibboleth.HttpServletResponse" />
+ class="net.shibboleth.oidc.profile.encoder.impl.HTTPRedirectAuthnRequestEncoder" init-method=""
+ scope="prototype" p:httpServletResponse-ref="shibboleth.HttpServletResponse" />
- <bean id="HTTPPostAuthnRequestEncoder" class="net.shibboleth.oidc.profile.encoder.impl.HTTPPostAuthnRequestEncoder"
- init-method="" scope="prototype" p:velocityEngine-ref="shibboleth.VelocityEngine"
- p:httpServletResponse-ref="shibboleth.HttpServletResponse" />
+ <bean id="HTTPPostAuthnRequestEncoder"
+ class="net.shibboleth.oidc.profile.encoder.impl.HTTPPostAuthnRequestEncoder" init-method="" scope="prototype"
+ p:velocityEngine-ref="shibboleth.VelocityEngine" p:httpServletResponse-ref="shibboleth.HttpServletResponse" />
<bean id="EncodeMessage" class="org.opensaml.profile.action.impl.EncodeMessage" scope="prototype"
- p:messageEncoderFactory-ref="messageEncoderFactory" p:httpServletResponse-ref="shibboleth.HttpServletResponse"/>
-
+ p:messageEncoderFactory-ref="messageEncoderFactory" p:httpServletResponse-ref="shibboleth.HttpServletResponse" />
+
<!-- TODO: Place holder for message handlers -->
- <bean id="PreEncodeMessageHandler" class="org.opensaml.messaging.handler.impl.BasicMessageHandlerChain" scope="prototype">
+ <bean id="PreEncodeMessageHandler" class="org.opensaml.messaging.handler.impl.BasicMessageHandlerChain"
+ scope="prototype">
<property name="handlers">
<list>
-
+
</list>
</property>
</bean>
-
+
<!-- Message Decoding -->
<bean id="messageDecoderFactory" class="net.shibboleth.idp.saml.profile.impl.SpringAwareMessageDecoderFactory">
<property name="beanMappings">
@@ -158,71 +151,82 @@
</map>
</property>
</bean>
-
+
<bean id="OIDCRedirectAuthnResponseDecoder"
- class="net.shibboleth.oidc.profile.decoding.impl.HTTPRedirectAuthnResponseDecoder" init-method="" scope="prototype"
- p:httpServletRequest-ref="shibboleth.HttpServletRequest" />
-
+ class="net.shibboleth.oidc.profile.decoding.impl.HTTPRedirectAuthnResponseDecoder" init-method=""
+ scope="prototype" p:httpServletRequest-ref="shibboleth.HttpServletRequest" />
+
<bean id="OIDCPostAuthnResponseDecoder"
class="net.shibboleth.oidc.profile.decoding.impl.HTTPPostAuthnResponseDecoder" init-method="" scope="prototype"
p:httpServletRequest-ref="shibboleth.HttpServletRequest" />
-
-
-
+
+
+
<!-- After authentication response -->
-
+
<bean id="ValidateExternalAuthenticationContext" scope="prototype"
- class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.ValidateExternalAuthenticationContext" />
-
- <bean id="ValidateAuthenticationResponseResult" scope="prototype"
- class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.ValidateAuthenticationResponseResult"
- p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext"
- p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup"/>
-
- <bean id="ValidateResponseStateMatchesRequest" scope="prototype"
+ class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.ValidateExternalAuthenticationContext" />
+
+ <bean id="ValidateAuthenticationResponseResult" parent="NestedWebFlowProfileActionAdaptor" scope="prototype"
+ class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.ValidateAuthenticationResponseResult"
+ p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup" />
+
+ <bean id="ValidateResponseStateMatchesRequest" parent="NestedWebFlowProfileActionAdaptor" scope="prototype"
class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.ValidateResponseState"
- p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext"
- p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup"/>
-
-
- <!-- CODE flow beans -->
-
- <bean id="InitializeOAuth2ClientAuthenticationContext" scope="prototype"
- class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.InitializeOAuth2ClientAuthenticationContext"
- p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext"/>
-
-
- <bean id="ExchangeCodeForAccessToken" scope="prototype"
- class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.ExchangeCodeForAccessToken"
+ p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup" />
+
+ <!-- Create a new OIDCPeerEntityContext and add it to the existing inbound context -->
+ <bean id="AddPeerEntityContextToInboundMessage"
+ class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.PrepareOIDCInboundMessageContext" scope="prototype"
p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext"
+ p:addToExistingInboundMessageContext="true">
+ <property name="identiferLookupStrategy">
+ <bean parent="shibboleth.Functions.Compose" c:f-ref="ParentAuthenticiationContextLookup">
+ <constructor-arg name="g">
+ <bean parent="shibboleth.Functions.Expression"
+ c:expression="#input.getAuthenticatingAuthority()" />
+ </constructor-arg>
+ </bean>
+ </property>
+ </bean>
+
+
+ <!-- CODE flow beans -->
+
+ <bean id="InitializeOAuth2ClientAuthenticationContext" parent="NestedWebFlowProfileActionAdaptor"
+ scope="prototype"
+ class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.InitializeOAuth2ClientAuthenticationContext" />
+
+
+ <bean id="ExchangeCodeForAccessToken" parent="NestedWebFlowProfileActionAdaptor" scope="prototype"
+ class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.ExchangeCodeForAccessToken"
p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup"
p:httpClient="#{getObject('shibboleth.authn.oidc.rp.HttpClient') ?: getObject('shibboleth.InternalHttpClient')}"
p:httpClientSecurityParameters="#{getObject('shibboleth.authn.oidc.rp.HttpClientSecurityParameters')}"
p:httpResponseDecoderStrategy="#{getObject('shibboleth.authn.oidc.rp.TokenResponseDecoder') ?: getObject('shibboleth.authn.oidc.rp.DefaultTokenResponseDecoder')}"
- p:httpRequestEncoderStrategy="#{getObject('shibboleth.authn.oidc.rp.TokenRequestEncoder') ?: getObject('shibboleth.authn.oidc.rp.DefaultAuthCodeTokenResponseEncoder')}"/>
-
- <bean id="ValidateOAuthAccessTokenResponse"
+ p:httpRequestEncoderStrategy="#{getObject('shibboleth.authn.oidc.rp.TokenRequestEncoder') ?: getObject('shibboleth.authn.oidc.rp.DefaultAuthCodeTokenResponseEncoder')}" />
+
+ <bean id="ValidateOAuthAccessTokenResponse" parent="NestedWebFlowProfileActionAdaptor" scope="prototype"
class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.ValidateOAuthAccessTokenResponse"
- p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup"
- p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext"/>
-
- <bean id="ExtractIDTokenFromTokenResponse" scope="prototype"
+ p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup" />
+
+ <bean id="ExtractIDTokenFromTokenResponse" parent="NestedWebFlowProfileActionAdaptor" scope="prototype"
class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.ExtractIDTokenFromResponse"
p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext"
- p:rawIdTokenLookupStrategy-ref="TokenResponseIDTokenLookupStrategy"/>
-
- <bean id="TokenResponseIDTokenLookupStrategy"
- class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.TokenResponseIDTokenLookupStrategy"/>
-
- <!-- could these be singletons? -->
- <bean id="shibboleth.authn.oidc.rp.DefaultTokenResponseDecoder" scope="prototype"
+ p:rawIdTokenLookupStrategy-ref="TokenResponseIDTokenLookupStrategy" />
+
+ <bean id="TokenResponseIDTokenLookupStrategy"
+ class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.TokenResponseIDTokenLookupStrategy" />
+
+ <!-- could these be singletons? -->
+ <bean id="shibboleth.authn.oidc.rp.DefaultTokenResponseDecoder" scope="prototype"
class="net.shibboleth.idp.plugin.authn.oidc.rp.decoding.impl.DefaultMapResponseDecoder"
- p:objectMapper-ref="shibboleth.authn.oidc.rp.JSONObjectMapper"/>
-
- <bean id="shibboleth.authn.oidc.rp.DefaultAuthCodeTokenResponseEncoder" scope="prototype"
- class="net.shibboleth.idp.plugin.authn.oidc.rp.encoding.impl.NimbusAuthCodeTokenRequestEncoder"/>
-
- <!-- Create a default object mapper. Setup should not change once injected -->
+ p:objectMapper-ref="shibboleth.authn.oidc.rp.JSONObjectMapper" />
+
+ <bean id="shibboleth.authn.oidc.rp.DefaultAuthCodeTokenResponseEncoder" scope="prototype"
+ class="net.shibboleth.idp.plugin.authn.oidc.rp.encoding.impl.NimbusAuthCodeTokenRequestEncoder" />
+
+ <!-- Create a default object mapper. Setup should not change once injected -->
<bean id="shibboleth.authn.oidc.rp.JSONObjectMapper" class="com.fasterxml.jackson.databind.ObjectMapper" />
<bean class="org.springframework.beans.factory.config.MethodInvokingBean"
@@ -244,258 +248,287 @@
<property name="arguments">
<bean class="java.text.SimpleDateFormat" c:_0="yyyy-MM-dd'T'HH:mm:ss.SSSZZ" />
</property>
- </bean>
-
-
+ </bean>
+
+
<!-- Process token -->
-
- <!-- <bean id="PopulateTokenSignatureSigningParameters"
- class="net.shibboleth.idp.plugin.oidc.op.profile.impl.PopulateOIDCSignatureSigningParameters " scope="prototype"
- p:configurationLookupStrategy-ref="shibboleth.SignatureSigningConfigurationLookup"
- p:signatureSigningParametersResolver-ref="shibboleth.oidc.TokenSignatureSigningParametersResolver">
- <property name="securityParametersContextLookupStrategy">
- <bean parent="shibboleth.Functions.Compose"
- c:g-ref="shibboleth.ChildLookupOrCreate.SecurityParameters"
- c:f-ref="shibboleth.ChildLookup.RelyingParty" />
- </property>
- <property name="existingParametersContextLookupStrategy">
- <bean parent="shibboleth.Functions.Compose"
- c:g-ref="shibboleth.ChildLookup.SecurityParameters"
- c:f-ref="shibboleth.MessageContextLookup.Outbound" />
- </property>
- </bean> -->
-
+
+ <bean id="PopulateIDTokenSignatureValidationParameters" parent="NestedWebFlowProfileActionAdaptor"
+ scope="prototype">
+ <constructor-arg>
+ <bean class="net.shibboleth.oidc.security.impl.GenericPopulateSignatureValidationParameters"
+ p:configurationLookupStrategy-ref="shibboleth.authn.oidc.rp.IDTokenSignatureValidationConfigurationLookup"
+ p:signatureValidationParametersResolver-ref="shibboleth.authn.oidc.rp.IDTokenJwtSignatureValidationParametersResolver" />
+ </constructor-arg>
+ </bean>
+
+ <bean id="shibboleth.authn.oidc.rp.IDTokenJwtSignatureValidationParametersResolver"
+ class="net.shibboleth.oidc.security.impl.OIDCProviderConfigurationSignatureValidationParametersResolver" />
+
+ <bean id="shibboleth.authn.oidc.rp.IDTokenSignatureValidationConfigurationLookup"
+ class="net.shibboleth.idp.plugin.authn.oidc.rp.config.navigate.IDTokenSignatureValidationConfigurationLookupFunction"
+ p:relyingPartyConfigurationResolver-ref="shibboleth.RelyingPartyConfigurationResolver" />
+
+
+ <bean id="HandleIDTokenSignature" parent="NestedWebFlowMessageHandlerAdaptor" scope="prototype"
+ c:executionDirection="INBOUND">
+ <constructor-arg>
+ <bean class="org.opensaml.messaging.handler.impl.BasicMessageHandlerChain">
+ <property name="handlers">
+ <list>
+ <bean
+ class="net.shibboleth.idp.plugin.authn.oidc.rp.metadata.impl.OIDCProviderMetadataLookupHandler"
+ scope="prototype"
+ p:copyContextStrategy-ref="OutboundOIDCMetadataContextLookup"
+ p:providerMetadataResolver-ref="shibboleth.oidc.rp.ProviderMetadataResolver" />
+ <bean class="net.shibboleth.oidc.security.impl.JWTMessageSignatureSecurityHandler"
+ scope="prototype">
+ <property name="jwtTokenLookupStrategy">
+ <bean
+ class="net.shibboleth.idp.profile.context.navigate.SpringExpressionContextLookupFunction"
+ c:_0="#{ T(org.opensaml.messaging.context.MessageContext) }"
+ c:outputType="#{T(com.nimbusds.jwt.SignedJWT)}"
+ c:expression="#input.getSubcontext(T(net.shibboleth.idp.plugin.authn.oidc.rp.context.AccessTokenResponseContext)).getIdToken()" />
+ </property>
+ <property name="providerMetadataLookupStrategy">
+ <ref bean="shibboleth.ChildLookup.OIDCProviderMetadataFromPeerEntityContext" />
+ </property>
+ </bean>
+ </list>
+ </property>
+ </bean>
+ </constructor-arg>
+ </bean>
+
+
+
+ <bean id="OutboundOIDCMetadataContextLookup" parent="shibboleth.Functions.Compose">
+ <constructor-arg name="g">
+ <bean class="org.opensaml.messaging.context.navigate.ChildContextLookup"
+ c:type="#{ T(net.shibboleth.oidc.metadata.context.OIDCProviderMetadataContext) }" />
+ </constructor-arg>
+ <constructor-arg name="f">
+ <bean parent="shibboleth.Functions.Compose">
+ <constructor-arg name="g">
+ <bean class="org.opensaml.messaging.context.navigate.ChildContextLookup"
+ c:type="#{ T(net.shibboleth.idp.plugin.authn.oidc.rp.context.OIDCPeerEntityContext) }" />
+ </constructor-arg>
+ <constructor-arg name="f">
+ <bean class="org.opensaml.messaging.context.navigate.MessageContextLookup"
+ c:direction="OUTBOUND" />
+ </constructor-arg>
+ </bean>
+ </constructor-arg>
+ </bean>
+
+ <!-- these are part of the global system just tmp for now -->
+ <bean id="shibboleth.SignatureValidationConfigurationLookup" lazy-init="true"
+ class="net.shibboleth.idp.profile.config.navigate.SignatureValidationConfigurationLookupFunction"
+ p:relyingPartyConfigurationResolver-ref="shibboleth.RelyingPartyConfigurationResolver" />
+ <bean id="shibboleth.SignatureValidationParametersResolver"
+ class="org.opensaml.xmlsec.impl.BasicSignatureValidationParametersResolver" />
+
<!-- Default id_token JWT validation wiring. -->
-
- <!-- No default cleanup, maybe could be to remove nonce etc. -->
- <bean id="ValidateIDTokenClaims" scope="prototype"
- class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.ValidateTokenClaims"
- p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext"
- p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup"
- p:cleanupHook="#{getObject('shibboleth.authn.oidc.rp.idtoken.jwt.claims.CleanUpHook')
+
+ <!-- No default cleanup, maybe could be to remove nonce etc. -->
+ <bean id="ValidateIDTokenClaims" parent="NestedWebFlowProfileActionAdaptor" scope="prototype"
+ class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.ValidateTokenClaims"
+ p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup"
+ p:cleanupHook="#{getObject('shibboleth.authn.oidc.rp.idtoken.jwt.claims.CleanUpHook')
?: getObject('DefaultCleanupHook')}"
- p:claimsValidator="#{getObject('shibboleth.authn.oidc.rp.idtoken.IDTokenClaimsValidator')
+ p:claimsValidator="#{getObject('shibboleth.authn.oidc.rp.idtoken.IDTokenClaimsValidator')
?: getObject('DefaultIDTokenClaimsValidator')}"
p:jwtLookupStrategy="#{getObject('shibboleth.authn.oidc.rp.idtoken.IDTokenLookupStrategy')
- ?: getObject('DefaultIDTokenLookupStrategy')}"/>
-
- <bean id="DefaultIDTokenLookupStrategy"
- class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.DefaultIDTokenLookupStrategy"/>
+ ?: getObject('DefaultIDTokenLookupStrategy')}" />
+
+ <bean id="DefaultIDTokenLookupStrategy"
+ class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.DefaultIDTokenLookupStrategy" />
<bean id="DefaultIDTokenClaimsValidator"
class="net.shibboleth.oidc.security.jwt.claims.impl.ChainingJWTClaimsValidator"
p:claimValidators-ref="IDTokenClaimsValidators" />
-
+
<bean id="OIDCProviderMetadataContextChildLookup"
class="org.opensaml.messaging.context.navigate.ChildContextLookup"
c:type="#{ T(net.shibboleth.oidc.metadata.context.OIDCProviderMetadataContext) }" />
- <bean id="ExpiryClaimsValidator"
- class="net.shibboleth.oidc.security.jwt.claims.impl.ExpiryClaimsValidator"
+ <bean id="ExpiryClaimsValidator" class="net.shibboleth.oidc.security.jwt.claims.impl.ExpiryClaimsValidator"
p:clockSkew="%{idp.policy.clockSkew:PT1M}" />
<bean id="NotBeforeClaimsValidator"
class="net.shibboleth.oidc.security.jwt.claims.impl.NotBeforeClaimsValidator"
p:clockSkew="%{idp.policy.clockSkew:PT1M}" />
-
- <bean id="IssuedAtClaimsValidator"
- class="net.shibboleth.oidc.security.jwt.claims.impl.IssuedAtClaimsValidator"
- p:clockSkew="%{idp.policy.clockSkew:PT1M}"
- p:messageLifetime="%{idp.policy.messageLifetime:PT1M}"
+
+ <bean id="IssuedAtClaimsValidator" class="net.shibboleth.oidc.security.jwt.claims.impl.IssuedAtClaimsValidator"
+ p:clockSkew="%{idp.policy.clockSkew:PT1M}" p:messageLifetime="%{idp.policy.messageLifetime:PT1M}"
p:requiredRule="false" />
- <bean id="IssuerClaimsValidator"
- class="net.shibboleth.oidc.security.jwt.claims.impl.ExactMatchClaimsValidator"
+ <bean id="IssuerClaimsValidator" class="net.shibboleth.oidc.security.jwt.claims.impl.ExactMatchClaimsValidator"
p:claimName="iss" p:valueToMatchLookupStrategy-ref="IssuerIDFromOIDCProviderMetadataContextLookupFunction" />
-
- <!-- check AZP is required if more than one audience value -->
- <bean id="AzpClaimRequiredValidator"
- class="net.shibboleth.oidc.security.jwt.claims.impl.RequiredClaimsValidator">
- <property name="activationCondition">
- <bean id="MultipleValuesExist"
+
+ <!-- check AZP is required if more than one audience value -->
+ <bean id="AzpClaimRequiredValidator"
+ class="net.shibboleth.oidc.security.jwt.claims.impl.RequiredClaimsValidator">
+ <property name="activationCondition">
+ <bean id="MultipleValuesExist"
class="net.shibboleth.oidc.security.jwt.claims.impl.NumberOfClaimValuesActivationCondition"
- c:claimToCheck="aud"
- c:numberOfValuesPredicate-ref="ManyValuesPredicate"/>
- </property>
- <property name="requiredClaims">
+ c:claimToCheck="aud" c:numberOfValuesPredicate-ref="ManyValuesPredicate" />
+ </property>
+ <property name="requiredClaims">
<list>
<value>azp</value>
</list>
- </property>
+ </property>
</bean>
-
+
<!-- TODO, seems like this could be done in XML somehow -->
- <bean id="ManyValuesPredicate"
+ <bean id="ManyValuesPredicate"
class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.ManyValuesIntegerComparisonPredicate" />
-
- <bean id="AzpClaimsValidator"
- class="net.shibboleth.oidc.security.jwt.claims.impl.ExactMatchClaimsValidator"
- p:claimName="azp" p:valueToMatchLookupStrategy-ref="ClientIDFromOAuth2ClientContextFunction">
- <property name="activationCondition">
+
+ <bean id="AzpClaimsValidator" class="net.shibboleth.oidc.security.jwt.claims.impl.ExactMatchClaimsValidator"
+ p:claimName="azp" p:valueToMatchLookupStrategy-ref="ClientIDFromOAuth2ClientContextFunction">
+ <property name="activationCondition">
<bean id="AzpClaimExistsCondition"
- class="net.shibboleth.oidc.security.jwt.claims.impl.ClaimExistsActivationCondition"
- c:claimToCheck="azp"/>
+ class="net.shibboleth.oidc.security.jwt.claims.impl.ClaimExistsActivationCondition" c:claimToCheck="azp" />
</property>
</bean>
-
+
<bean id="IssuerIDFromOIDCProviderMetadataContextLookupFunction"
class="net.shibboleth.oidc.profile.logic.IssuerIDFromOIDCProviderMetadataContextLookupFunction"
- p:oIDCMetadataContextLookupStrategy-ref="OIDCProviderMetadataContextFromOutboundPeerLookupStrategy"/>
-
- <!-- TODO This bean could be replaced by XML functions? -->
+ p:oIDCMetadataContextLookupStrategy-ref="OIDCProviderMetadataContextFromOutboundPeerLookupStrategy" />
+
+ <!-- TODO This bean could be replaced by XML functions? Could also be from the inbound message context at this point -->
<bean id="OIDCProviderMetadataContextFromOutboundPeerLookupStrategy"
- class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.OIDCProviderMetadataFromOuboundPeerLookupStrategy"/>
+ class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.OIDCProviderMetadataFromOuboundPeerLookupStrategy" />
+
+ <bean id="AudienceClaimsValidator" class="net.shibboleth.oidc.security.jwt.claims.impl.AudienceClaimsValidator"
+ p:audienceLookupStrategy-ref="ClientIDFromOAuth2ClientContextFunction" />
+
- <bean id="AudienceClaimsValidator"
- class="net.shibboleth.oidc.security.jwt.claims.impl.AudienceClaimsValidator"
- p:audienceLookupStrategy-ref="ClientIDFromOAuth2ClientContextFunction"/>
-
-
<bean id="ClientIDFromOAuth2ClientContextFunction"
class="net.shibboleth.idp.plugin.authn.oidc.rp.config.navigate.ClientIDFromOAuth2ClientContextFunction"
- c:oauth2ClientContextLookupStrategy-ref="shibboleth.ChildLookup.OAuth2ClientContextFromOutbound"/>
-
-
-
- <bean id="NonceClaimValidator"
- class="net.shibboleth.oidc.security.jwt.claims.impl.ExactMatchClaimsValidator"
- p:claimName="nonce"
- p:valueToMatchLookupStrategy="#{getObject('shibboleth.authn.oidc.rp.jwt.NonceLookupStrategy') ?:
+ c:oauth2ClientContextLookupStrategy-ref="shibboleth.ChildLookup.OAuth2ClientContextFromOutbound" />
+
+
+
+ <bean id="NonceClaimValidator" class="net.shibboleth.oidc.security.jwt.claims.impl.ExactMatchClaimsValidator"
+ p:claimName="nonce"
+ p:valueToMatchLookupStrategy="#{getObject('shibboleth.authn.oidc.rp.jwt.NonceLookupStrategy') ?:
getObject('shibboleth.authn.oidc.rp.jwt.DefaultNonceLookupStrategy')}"
- p:activationCondition="#{getObject('shibboleth.authn.oidc.rp.jwt.NonceActivationCondition') ?:
- getObject('shibboleth.authn.oidc.rp.jwt.DefaultNonceActivationCondition')}"/>
-
- <bean id="shibboleth.authn.oidc.rp.jwt.DefaultNonceActivationCondition"
- class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.NonceValidationActivationCondition"/>
-
- <bean id="shibboleth.authn.oidc.rp.jwt.DefaultNonceLookupStrategy"
- class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.AuthenticationRequestNonceClaimLookupStrategy"/>
-
-
- <bean id="OIDCMetadataContextChildLookup"
- class="org.opensaml.messaging.context.navigate.ChildContextLookup"
+ p:activationCondition="#{getObject('shibboleth.authn.oidc.rp.jwt.NonceActivationCondition') ?:
+ getObject('shibboleth.authn.oidc.rp.jwt.DefaultNonceActivationCondition')}" />
+
+ <bean id="shibboleth.authn.oidc.rp.jwt.DefaultNonceActivationCondition"
+ class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.NonceValidationActivationCondition" />
+
+ <bean id="shibboleth.authn.oidc.rp.jwt.DefaultNonceLookupStrategy"
+ class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.AuthenticationRequestNonceClaimLookupStrategy" />
+
+
+ <bean id="OIDCMetadataContextChildLookup" class="org.opensaml.messaging.context.navigate.ChildContextLookup"
c:type="#{ T(net.shibboleth.oidc.metadata.context.OIDCMetadataContext) }" />
-
- <bean id="OIDCPeerEntityContextChildLookup"
- class="org.opensaml.messaging.context.navigate.ChildContextLookup"
+
+ <bean id="OIDCPeerEntityContextChildLookup" class="org.opensaml.messaging.context.navigate.ChildContextLookup"
c:type="#{ T(net.shibboleth.idp.plugin.authn.oidc.rp.context.OIDCPeerEntityContext) }" />
-
+
<util:list id="IDTokenClaimsValidators" value-type="net.shibboleth.oidc.jwt.claims.ClaimsValidator">
<ref bean="IssuerClaimsValidator" /> <!-- TODO prevent: if it contains additional audiences not trusted by the Client. -->
- <ref bean="AudienceClaimsValidator" />
- <ref bean="AzpClaimRequiredValidator"/>
- <ref bean="AzpClaimsValidator"/>
+ <ref bean="AudienceClaimsValidator" />
+ <ref bean="AzpClaimRequiredValidator" />
+ <ref bean="AzpClaimsValidator" />
<ref bean="ExpiryClaimsValidator" />
- <ref bean="IssuedAtClaimsValidator" />
+ <ref bean="IssuedAtClaimsValidator" />
<ref bean="NotBeforeClaimsValidator" />
<ref bean="NonceClaimValidator" />
<!-- missing ACR? and auth_time, access_token at_hash. -->
- </util:list>
-
-
+ </util:list>
+
+
<bean id="CheckUserInfoRequiredCondition"
- class="net.shibboleth.idp.plugin.authn.oidc.rp.messaging.context.logic.UserInfoLookupCondition"/>
-
-
+ class="net.shibboleth.idp.plugin.authn.oidc.rp.messaging.context.logic.UserInfoLookupCondition" />
+
+
<!-- UserInfo endpoint beans -->
-
- <bean id="UserInfoEndpointLookup" class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.UserInfoEndpointLookup"
- p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext"
+
+ <bean id="UserInfoEndpointLookup" parent="NestedWebFlowProfileActionAdaptor" scope="prototype"
+ class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.UserInfoEndpointLookup"
p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup"
p:httpClient="#{getObject('shibboleth.authn.oidc.rp.HttpClient') ?: getObject('shibboleth.InternalHttpClient')}"
p:httpClientSecurityParameters="#{getObject('shibboleth.authn.oidc.rp.HttpClientSecurityParameters')}"
p:httpResponseDecoderStrategy="#{getObject('shibboleth.authn.oidc.rp.UserInfoResponseDecoder') ?: getObject('shibboleth.authn.oidc.rp.DefaultUserInfoResponseDecoder')}"
- p:httpRequestEncoderStrategy="#{getObject('shibboleth.authn.oidc.rp.UserInfoRequestEncoder') ?: getObject('shibboleth.authn.oidc.rp.DefaultUserInfoRequestEncoder')}"/>
-
-
- <bean id="shibboleth.authn.oidc.rp.DefaultUserInfoResponseDecoder" scope="prototype"
+ p:httpRequestEncoderStrategy="#{getObject('shibboleth.authn.oidc.rp.UserInfoRequestEncoder') ?: getObject('shibboleth.authn.oidc.rp.DefaultUserInfoRequestEncoder')}" />
+
+
+ <bean id="shibboleth.authn.oidc.rp.DefaultUserInfoResponseDecoder" scope="prototype"
class="net.shibboleth.idp.plugin.authn.oidc.rp.decoding.impl.DefaultUserInfoResponseDecoder"
- p:objectMapper-ref="shibboleth.authn.oidc.rp.JSONObjectMapper"/>
-
- <bean id="shibboleth.authn.oidc.rp.DefaultUserInfoRequestEncoder" scope="prototype"
- class="net.shibboleth.idp.plugin.authn.oidc.rp.encoding.impl.DefaultUserInfoRequestEncoder"/>
-
-
- <bean id="ValidateUserInfoClaims"
- class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.ValidateUserInfoClaims" scope="prototype"
- p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext"
- p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup"/>
-
-
- <bean id="ProcessEndUserClaims"
- class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.ProcessEndUserClaims" scope="prototype"
- p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext"
- p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup"/>
-
+ p:objectMapper-ref="shibboleth.authn.oidc.rp.JSONObjectMapper" />
+
+ <bean id="shibboleth.authn.oidc.rp.DefaultUserInfoRequestEncoder" scope="prototype"
+ class="net.shibboleth.idp.plugin.authn.oidc.rp.encoding.impl.DefaultUserInfoRequestEncoder" />
+
+
+ <bean id="ValidateUserInfoClaims" parent="NestedWebFlowProfileActionAdaptor" scope="prototype"
+ class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.ValidateUserInfoClaims"
+ p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup" />
+
+
+ <bean id="ProcessEndUserClaims" class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.ProcessEndUserClaims"
+ scope="prototype" p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext"
+ p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup" />
+
<bean id="CheckUserInfoSignedJWTResponseTypeCondition"
- class="net.shibboleth.idp.plugin.authn.oidc.rp.messaging.context.logic.UserInfoSignedJWTResponseTypeCondition"/>
-
+ class="net.shibboleth.idp.plugin.authn.oidc.rp.messaging.context.logic.UserInfoSignedJWTResponseTypeCondition" />
+
<bean id="CheckUserInfoEncryptedJWTResponseTypeCondition"
- class="net.shibboleth.idp.plugin.authn.oidc.rp.messaging.context.logic.UserInfoEncryptedJWTResponseTypeCondition"/>
-
-
+ class="net.shibboleth.idp.plugin.authn.oidc.rp.messaging.context.logic.UserInfoEncryptedJWTResponseTypeCondition" />
+
+
<!-- UserInfo response JWT validation -->
-
- <bean id="ValidateUserInfoToken" scope="prototype"
- class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.ValidateTokenClaims"
- p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext"
- p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup"
- p:cleanupHook="#{getObject('shibboleth.authn.oidc.rp.userinfo.jwt.claims.CleanUpHook')}"
- p:claimsValidator="#{getObject('shibboleth.authn.oidc.rp.userinfo.UserInfoTokenClaimsValidator')
+
+ <bean id="ValidateUserInfoToken" parent="NestedWebFlowProfileActionAdaptor" scope="prototype"
+ class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.ValidateTokenClaims"
+ p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup"
+ p:cleanupHook="#{getObject('shibboleth.authn.oidc.rp.userinfo.jwt.claims.CleanUpHook')}"
+ p:claimsValidator="#{getObject('shibboleth.authn.oidc.rp.userinfo.UserInfoTokenClaimsValidator')
?: getObject('DefaultUserInfoTokenClaimsValidator')}"
p:jwtLookupStrategy="#{getObject('shibboleth.authn.oidc.rp.userinfo.UserInfoTokenLookupStrategy')
- ?: getObject('DefaultUserInfoTokenLookupStrategy')}"/>
-
+ ?: getObject('DefaultUserInfoTokenLookupStrategy')}" />
+
<bean id="DefaultUserInfoTokenClaimsValidator"
class="net.shibboleth.oidc.security.jwt.claims.impl.ChainingJWTClaimsValidator"
p:claimValidators-ref="UserInfoClaimsValidators" />
-
- <bean id="DefaultUserInfoTokenLookupStrategy"
- class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.DefaultUserInfoTokenLookupStrategy"/>
+
+ <bean id="DefaultUserInfoTokenLookupStrategy"
+ class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.DefaultUserInfoTokenLookupStrategy" />
<util:list id="UserInfoClaimsValidators" value-type="net.shibboleth.oidc.jwt.claims.ClaimsValidator">
<ref bean="IssuerClaimsValidator" />
- <ref bean="AudienceClaimsValidator" />
- </util:list>
-
-
-
+ <ref bean="AudienceClaimsValidator" />
+ </util:list>
+
+
+
<!-- Final validation and proxy authentication result -->
-
- <bean id="ValidateOIDCAuthentication"
- class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.ValidateOIDCAuthentication" scope="prototype"
- p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext"
+
+ <bean id="ValidateOIDCAuthentication" parent="NestedWebFlowProfileActionAdaptor"
+ class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.ValidateOIDCAuthentication" scope="prototype"
p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup"
p:responderLookupStrategy-ref="shibboleth.RelyingPartyIdLookup.Simple"
p:requesterLookupStrategy-ref="shibboleth.ResponderIdLookup.Simple"
p:attributeFilter-ref="shibboleth.AttributeFilterService"
- p:transcoderRegistry-ref="shibboleth.AttributeRegistryService"/>
-
-
-
-
-
-
-
-
-
-
-
-
-
- <!-- OLD STUFF -->
-
- <bean id="ValidateIDTokenSignature"
- class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.ValidateIDTokenSignature" />
+ p:transcoderRegistry-ref="shibboleth.AttributeRegistryService" />
+
+ <!-- OLD STUFF -->
+
+
<bean id="shibboleth.authn.oidc.rp.jwt.DefaultAuthTimeActivationCondition"
- class="net.shibboleth.oidc.security.jwt.claims.impl.ForcedAuthenticationActivationCondition"/>
-
-
- <!-- These represent the default set of id_token claims which are **required** by OIDC
- https://openid.net/specs/openid-connect-core-1_0.html#IDToken -->
+ class="net.shibboleth.oidc.security.jwt.claims.impl.ForcedAuthenticationActivationCondition" />
+
+
+ <!-- These represent the default set of id_token claims which are **required** by OIDC https://openid.net/specs/openid-connect-core-1_0.html#IDToken -->
<util:set id="shibboleth.authn.oidc.rp.DefaultRequiredOIDCClaims">
<value>iss</value>
<value>sub</value>
@@ -503,5 +536,5 @@
<value>exp</value>
<value>iat</value>
</util:set>
-
+
</beans>
diff --git a/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-flow.xml b/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-flow.xml
index 6e5cd49..f69d904 100644
--- a/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-flow.xml
+++ b/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-flow.xml
@@ -60,6 +60,10 @@
<evaluate expression="ValidateExternalAuthenticationContext"/>
<evaluate expression="ValidateAuthenticationResponseResult"/>
<evaluate expression="ValidateResponseStateMatchesRequest"/>
+ <!-- Add a new OIDCPeerEntityContext to inbound authentication response context
+ using the original authenticating authority. The OIDC response does not contain an issuer (this is
+ later tested in the id_token) matched against the original provider metadata -->
+ <evaluate expression="AddPeerEntityContextToInboundMessage"/>
<evaluate expression="'proceed'" />
<transition on="proceed" to="SwitchOnGrantType" />
</action-state>
@@ -98,7 +102,8 @@
<!-- TODO claim validation will differ per grant_type -->
<action-state id="ValidateToken">
- <!-- <evaluate expression="PopulateTokenSignatureSigningParameters" /> -->
+ <evaluate expression="PopulateIDTokenSignatureValidationParameters" />
+ <evaluate expression="HandleIDTokenSignature" />
<!-- <evaluate expression="PopulateTokenEncryptionParameters" /> -->
<evaluate expression="ValidateIDTokenClaims" />
<evaluate expression="'proceed'" />
@@ -128,7 +133,7 @@
</decision-state>
<action-state id="ValidateSignedUserInfoJWT">
- <!-- <evaluate expression="PopulateTokenSignatureSigningParameters" /> -->
+ <!-- <evaluate expression="PopulateUserInfoTokenSignatureValidationParameters" /> -->
<evaluate expression="ValidateUserInfoToken" />
<evaluate expression="'proceed'" />
<transition on="proceed" to="ValidateUserInfoClaimsSet" />
diff --git a/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml b/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml
index 5feae96..c1fadb1 100644
--- a/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml
+++ b/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml
@@ -64,8 +64,48 @@
/> </property> <property name="requestObjectSignatureValidationConfiguration"> <ref bean="#{'%{idp.oidc.rovalid.config:shibboleth.oidc.requestObjectSignatureValidationConfiguration}'.trim()}"
/> </property> <property name="tokenEndpointJwtSignatureValidationConfiguration"> <ref bean="#{'%{idp.oidc.rovalid.config:shibboleth.oidc.tokenEndpointJwtSignatureValidationConfiguration}'.trim()}"
/> </property> -->
+ <property name="idTokenJwtSignatureValidationConfig">
+ <ref bean="#{'%{idp.oidc.rp.rovalid.config:shibboleth.authn.oidc.rp.IDTokenJwtSignatureValidationConfiguration}'.trim()}"/>
+ </property>
</bean>
-
+
+
+ <!-- Configuration for supported algorithms for token endpoint authentication JWT signature validation. -->
+ <!-- TODO This was a parent bean, but as that was not compatible with the new trust engine stuff, I moved to it's own class for now -->
+ <bean id="shibboleth.authn.oidc.rp.IDTokenJwtSignatureValidationConfiguration"
+ class="net.shibboleth.oidc.security.impl.BasicSignatureValidationConfiguration"
+ p:signatureTrustEngine-ref="ExplicitKeySignedJWTTrustEngine">
+ <!-- <property name="signatureAlgorithms">
+ <list>
+ <util:constant
+ static-field="net.shibboleth.oidc.jwa.support.SignatureConstants.ALGO_ID_SIGNATURE_RS_256" />
+ <util:constant
+ static-field="net.shibboleth.oidc.jwa.support.SignatureConstants.ALGO_ID_SIGNATURE_RS_384" />
+ <util:constant
+ static-field="net.shibboleth.oidc.jwa.support.SignatureConstants.ALGO_ID_SIGNATURE_RS_512" />
+ <util:constant
+ static-field="net.shibboleth.oidc.jwa.support.SignatureConstants.ALGO_ID_SIGNATURE_ES_256" />
+ <util:constant
+ static-field="net.shibboleth.oidc.jwa.support.SignatureConstants.ALGO_ID_SIGNATURE_ES_384" />
+ <util:constant
+ static-field="net.shibboleth.oidc.jwa.support.SignatureConstants.ALGO_ID_SIGNATURE_ES_512" />
+ <util:constant
+ static-field="net.shibboleth.oidc.jwa.support.SignatureConstants.ALGO_ID_SIGNATURE_HS_256" />
+ <util:constant
+ static-field="net.shibboleth.oidc.jwa.support.SignatureConstants.ALGO_ID_SIGNATURE_HS_384" />
+ <util:constant
+ static-field="net.shibboleth.oidc.jwa.support.SignatureConstants.ALGO_ID_SIGNATURE_HS_512" />
+ </list>
+ </property> -->
+ </bean>
+
+ <bean id="ExplicitKeySignedJWTTrustEngine"
+ class="net.shibboleth.oidc.security.impl.ExplicitKeySignedJWTTrustEngine"
+ c:resolver-ref="OIDCProviderMetadataCredentialResolver"/>
+
+ <bean id="OIDCProviderMetadataCredentialResolver"
+ class="net.shibboleth.oidc.security.impl.ProviderMetadataCredentialResolver"
+ p:remoteJwkSetCache-ref="shibboleth.authn.oidc.rp.RemoteJwkSetCache"/>
</beans>
diff --git a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCRPFlowTest.java b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCRPFlowTest.java
index e49611f..3640245 100644
--- a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCRPFlowTest.java
+++ b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCRPFlowTest.java
@@ -28,6 +28,7 @@ import java.util.Map;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
+import javax.crypto.spec.SecretKeySpec;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.TrustAllStrategy;
@@ -36,6 +37,8 @@ import org.apache.http.ssl.SSLContextBuilder;
import org.junit.Test;
import org.opensaml.messaging.context.MessageContext;
import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.security.credential.Credential;
+import org.opensaml.security.credential.CredentialResolver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
@@ -46,6 +49,7 @@ import org.springframework.webflow.engine.impl.FlowExecutionImpl;
import org.springframework.webflow.execution.FlowExecution;
import org.springframework.webflow.test.MockFlowBuilderContext;
+import com.nimbusds.jose.Algorithm;
import com.nimbusds.jose.EncryptionMethod;
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JOSEObjectType;
@@ -84,7 +88,6 @@ import net.shibboleth.idp.authn.context.AuthenticationContext;
import net.shibboleth.idp.authn.context.ExternalAuthenticationContext;
import net.shibboleth.idp.authn.context.SubjectCanonicalizationContext;
import net.shibboleth.idp.authn.impl.ExternalAuthenticationImpl;
-import net.shibboleth.idp.plugin.authn.oidc.rp.config.StorageServiceBackedClientAuthenticationLookupStrategy;
import net.shibboleth.idp.plugin.authn.oidc.rp.context.OAuth2ClientContext;
import net.shibboleth.idp.plugin.authn.oidc.rp.context.OIDCPeerEntityContext;
import net.shibboleth.idp.plugin.authn.oidc.rp.context.ResponseTypeAndModeContext;
@@ -94,10 +97,16 @@ import net.shibboleth.idp.profile.context.RelyingPartyContext;
import net.shibboleth.idp.relyingparty.RelyingPartyConfiguration;
import net.shibboleth.oidc.metadata.context.OIDCProviderMetadataContext;
import net.shibboleth.oidc.profile.config.OIDCAuthorizationConfiguration;
+import net.shibboleth.oidc.profile.config.OIDCSecurityConfiguration;
import net.shibboleth.oidc.profile.core.OIDCAuthenticationRequest;
+import net.shibboleth.oidc.security.credential.BasicJWKCredential;
+import net.shibboleth.oidc.security.impl.BasicSignatureValidationConfiguration;
+import net.shibboleth.oidc.security.impl.ExplicitKeySignedJWTTrustEngine;
import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
import net.shibboleth.utilities.java.support.annotation.constraint.Unmodifiable;
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+import net.shibboleth.utilities.java.support.resolver.ResolverException;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import okhttp3.tls.HandshakeCertificates;
@@ -111,6 +120,8 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
private final String RP_ALLOWED_ORIGINS = "https://localhost";
private static final String CLIENT_ID = "demo_rp";
+
+ private static final String ID_TOKEN_HMAC_SECRET = "Xp2s5v8y/B?E(H+MbQeThWmYq3t6w9z$";
/**
* Example of good provider metadata. Endpoints are localhost to support the
@@ -306,7 +317,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
.build();
payload.getClaims().forEach((k,v) -> log.debug("{}:{}",k,v));
final var signedJWT = new SignedJWT(header,payload);
- signedJWT.sign(new MACSigner("Xp2s5v8y/B?E(H+MbQeThWmYq3t6w9z$"));
+ signedJWT.sign(new MACSigner(ID_TOKEN_HMAC_SECRET));
final String accessTokenSerialized = "{\n"
+ " \"access_token\": \"W0y5aDNAzEPNpSzu1cuMG904BZuQFZJUUwG5F3ct0zydZWy1ji\",\n"
+ " \"token_type\": \"Bearer\",\n"
@@ -614,6 +625,31 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
}
+ private OIDCSecurityConfiguration createSecurityConfigAndValidationParamsForHMAC(
+ @Nonnull final byte[] secret, @Nonnull final String algo) {
+
+ final var securityConfig = new OIDCSecurityConfiguration();
+ final var sigValConfig = new BasicSignatureValidationConfiguration<SignedJWT>();
+ final var credResolver = new CredentialResolver() {
+
+ @Override
+ public Credential resolveSingle(final CriteriaSet criteria) throws ResolverException {
+ final var cred = new BasicJWKCredential();
+ cred.setAlgorithm(Algorithm.parse(algo));
+ cred.setSecretKey(new SecretKeySpec(secret, algo));
+ return cred;
+ }
+
+ @Override
+ public Iterable<Credential> resolve(final CriteriaSet criteria) throws ResolverException {
+ return List.of(resolveSingle(criteria));
+ }
+ };
+ sigValConfig.setSignatureTrustEngine(new ExplicitKeySignedJWTTrustEngine(credResolver));
+ securityConfig.setIdTokenJwtSignatureValidationConfig(sigValConfig);
+ return securityConfig;
+ }
+
@Test
public void testAuthnFlowFromAuthorizationCallback_UsingSignedJWTUserInfoResponse() throws Exception {
@@ -653,6 +689,9 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
final RelyingPartyContext partyContext = new RelyingPartyContext();
final OIDCAuthorizationConfiguration partyConfig = new OIDCAuthorizationConfiguration();
partyContext.setProfileConfig(partyConfig);
+
+ partyConfig.setSecurityConfiguration(createSecurityConfigAndValidationParamsForHMAC(
+ ID_TOKEN_HMAC_SECRET.getBytes(), "HS256"));
partyConfig.setClientAuthenticationLookupStrategy(p ->
new ClientSecretBasic(new ClientID(CLIENT_ID), new Secret("secret")));
@@ -660,9 +699,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
rPartyConfig.setResponderId("http://idp.example.com/");
partyContext.setConfiguration(rPartyConfig);
nestPrc.addSubcontext(partyContext);
-
-
-
+
// Setup outbound context
final MessageContext outMsgCtx = new MessageContext();
outMsgCtx.setMessage(createAuthenticationRequest());
@@ -828,10 +865,12 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
// Add under nest PRC
final RelyingPartyContext partyContext = new RelyingPartyContext();
final OIDCAuthorizationConfiguration partyConfig = new OIDCAuthorizationConfiguration();
- partyConfig.setClientAuthenticationLookupStrategy(new StorageServiceBackedClientAuthenticationLookupStrategy());
partyContext.setProfileConfig(partyConfig);
partyConfig.setClientAuthenticationLookupStrategy(p ->
new ClientSecretBasic(new ClientID(CLIENT_ID), new Secret("secret")));
+ final RelyingPartyConfiguration rPartyConfig = new RelyingPartyConfiguration();
+ rPartyConfig.setResponderId("http://idp.example.com/");
+ partyContext.setConfiguration(rPartyConfig);
nestPrc.addSubcontext(partyContext);
// Setup outbound context
diff --git a/idp-oidc-rp-impl/src/test/resources/conf/test-relying-party-system.xml b/idp-oidc-rp-impl/src/test/resources/conf/test-relying-party-system.xml
index 8fbf822..ecf2f28 100644
--- a/idp-oidc-rp-impl/src/test/resources/conf/test-relying-party-system.xml
+++ b/idp-oidc-rp-impl/src/test/resources/conf/test-relying-party-system.xml
@@ -9,13 +9,13 @@
default-init-method="initialize" default-destroy-method="destroy">
- <!-- TODO: left this bean out of the tests
- p:defaultSecurityConfiguration-ref="%{idp.security.config:shibboleth.DefaultSecurityConfiguration}" -->
+ <!-- removed, but I do not know what the default should be in this case
+ p:defaultSecurityConfiguration-ref="%{idp.security.config:shibboleth.DefaultSecurityConfiguration}" -->
<bean class="net.shibboleth.idp.relyingparty.impl.DefaultRelyingPartyConfigurationResolver"
p:unverifiedConfiguration-ref="shibboleth.UnverifiedRelyingParty"
p:defaultConfiguration-ref="shibboleth.DefaultRelyingParty"
p:relyingPartyConfigurations-ref="shibboleth.RelyingPartyOverrides"
-
+
p:signingCredentials="#{getObject('shibboleth.SigningCredentials')}"
p:encryptionCredentials="#{getObject('shibboleth.EncryptionCredentials')}" />
@@ -47,7 +47,7 @@
</util:list>
- <!--
+ <!--
Map clients to appropriate client authentication - only supports client_secret_basic and client_secret_post
-->
diff --git a/pom.xml b/pom.xml
index 9cef199..618e747 100644
--- a/pom.xml
+++ b/pom.xml
@@ -16,10 +16,10 @@
<properties>
<idp.groupId>net.shibboleth.idp</idp.groupId>
- <idp.version>4.2.0-SNAPSHOT</idp.version>
+ <idp.version>4.2.2-SNAPSHOT</idp.version>
<opensaml.groupId>org.opensaml</opensaml.groupId>
- <opensaml.version>4.2.0-SNAPSHOT</opensaml.version>
- <oidc.common.version>2.0.0-SNAPSHOT</oidc.common.version>
+ <opensaml.version>4.2.1-SNAPSHOT</opensaml.version>
+ <oidc.common.version>2.1.0-SNAPSHOT</oidc.common.version>
<checkstyle.configLocation>${project.basedir}/checkstyle.xml</checkstyle.configLocation>
</properties>
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list