[java-oidc-common] 02/35: JCOMOIDC-41 - Move OIDC Signature Validation resolvers and parameter classes to commons

Phil Smart philip.smart at jisc.ac.uk
Tue Sep 20 14:19:02 UTC 2022


This is an automated email from the git hooks/post-receive script.

philsmart pushed a commit to branch dev/JCOMOIDC-41
in repository java-oidc-common.

View the commit online:
http://git.shibboleth.net/view/?p=java-oidc-common.git;a=commit;h=35d959706f0c0f978fbb5ecaf8a1c3ef8ca94697

commit 35d959706f0c0f978fbb5ecaf8a1c3ef8ca94697
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Fri May 6 14:18:01 2022 +0100

    JCOMOIDC-41 - Move OIDC Signature Validation resolvers and parameter
    classes to commons
    
    See https://shibboleth.atlassian.net/browse/JCOMOIDC-41
---
 .../security/SignatureValidationConfiguration.java |  41 +++
 .../security/SignatureValidationParameters.java    |  36 +++
 .../SignatureValidationParametersResolver.java     |  13 +
 .../context/JWTSecurityParametersContext.java      | 137 ++++++++++
 ...TSignatureValidationConfigurationCriterion.java | 109 ++++++++
 .../oidc/security/criterion/KeyIdCriterion.java    |  82 ++++++
 .../criterion/ProviderMetadataCriterion.java       |  69 +++++
 oidc-common-crypto-impl/pom.xml                    |   6 +
 .../impl/EvaluableKeyIDCredentialCriterion.java    |  98 +++++++
 .../impl/BaseJWTSignatureSecurityHandler.java      | 118 ++++++++
 .../security/impl/BaseSignedJWTTrustEngine.java    |  14 +-
 .../BasicSignatureValidationConfiguration.java     |  40 +++
 .../impl/ExplicitKeySignedJWTTrustEngine.java      |  25 ++
 .../impl/JWTMessageSignatureSecurityHandler.java   | 114 ++++++++
 ...ationSignatureValidationParametersResolver.java | 119 ++++++++
 .../PopulateJWTSignatureValidationParameters.java  | 142 ++++++++++
 ...ateJWTSignatureValidationParametersHandler.java | 164 +++++++++++
 .../impl/ProviderMetadataCredentialResolver.java   | 180 ++++++++++++
 .../impl/ExplicitKeySignedJWTTrustEngineTest.java  | 301 +++++++++++++++++++++
 .../config/OIDCAuthorizationConfiguration.java     | 283 ++++++++++++++-----
 .../profile/config/OIDCSecurityConfiguration.java  |  24 ++
 pom.xml                                            |   2 +-
 22 files changed, 2040 insertions(+), 77 deletions(-)

diff --git a/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/SignatureValidationConfiguration.java b/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/SignatureValidationConfiguration.java
new file mode 100644
index 0000000..b593989
--- /dev/null
+++ b/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/SignatureValidationConfiguration.java
@@ -0,0 +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.oidc.security;
+
+import javax.annotation.Nullable;
+
+import org.opensaml.security.trust.TrustEngine;
+import org.opensaml.xmlsec.AlgorithmPolicyConfiguration;
+
+/**
+ * The configuration information to use when validating a signature.
+ * 
+ * @param <T> the token type to validate
+ */
+//TODO We can not use the existing SignatureValidationConfiguration
+//as it implements the signaturetrustengine which is only compatible with XML.
+public interface SignatureValidationConfiguration<T> extends AlgorithmPolicyConfiguration {
+    
+    /**
+     * Get the signature trust engine to use.
+     * 
+     * @return the signature trust engine
+     */
+    @Nullable public TrustEngine<T> getSignatureTrustEngine();
+
+}
diff --git a/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/SignatureValidationParameters.java b/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/SignatureValidationParameters.java
new file mode 100644
index 0000000..e242b5f
--- /dev/null
+++ b/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/SignatureValidationParameters.java
@@ -0,0 +1,36 @@
+package net.shibboleth.oidc.security;
+
+import javax.annotation.Nullable;
+
+import org.opensaml.security.trust.TrustEngine;
+import org.opensaml.xmlsec.AlgorithmPolicyParameters;
+
+/**
+ * The effective parameters to use when validating a signature of the given type.
+ *
+ * @param <T> the token type this validation parameter 
+ */
+public class SignatureValidationParameters<T> extends AlgorithmPolicyParameters {
+    
+    /** The signature trust engine to use. */
+    @Nullable private TrustEngine<T> signatureTrustEngine;
+    
+    /**
+     * Get the signature trust engine to use.
+     * 
+     * @return the signature trust engine
+     */
+    @Nullable public TrustEngine<T> getSignatureTrustEngine() {
+        return signatureTrustEngine;
+    }
+
+    /**
+     * Set the signature trust engine to use.
+     * 
+     * @param engine the signature trust engine
+     */
+    public void setSignatureTrustEngine(@Nullable final TrustEngine<T> engine) {
+        signatureTrustEngine = engine;
+    }
+
+}
diff --git a/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/SignatureValidationParametersResolver.java b/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/SignatureValidationParametersResolver.java
new file mode 100644
index 0000000..2eb87f4
--- /dev/null
+++ b/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/SignatureValidationParametersResolver.java
@@ -0,0 +1,13 @@
+package net.shibboleth.oidc.security;
+
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+import net.shibboleth.utilities.java.support.resolver.Resolver;
+
+/**
+ * An interface for components which resolve {@link SignatureValidationParameters} based on a {@link CriteriaSet}.
+ * 
+ * @param <T> the token type.
+ */
+public interface SignatureValidationParametersResolver<T> extends Resolver<SignatureValidationParameters<T>, CriteriaSet> {
+
+}
diff --git a/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/context/JWTSecurityParametersContext.java b/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/context/JWTSecurityParametersContext.java
new file mode 100644
index 0000000..3c8ebc9
--- /dev/null
+++ b/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/context/JWTSecurityParametersContext.java
@@ -0,0 +1,137 @@
+/*
+ * 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.oidc.security.context;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.messaging.context.BaseContext;
+import org.opensaml.xmlsec.DecryptionParameters;
+import org.opensaml.xmlsec.EncryptionParameters;
+import org.opensaml.xmlsec.SignatureSigningParameters;
+
+import com.nimbusds.jwt.SignedJWT;
+
+import net.shibboleth.oidc.security.SignatureValidationParameters;
+
+/**
+ * A context implementation for storing parameter instances for JWT signature signing and validation, and JWT
+ * encryption and decryption.
+ */
+//TODO some of this is generic enough to use in any context?
+public class JWTSecurityParametersContext extends BaseContext { 
+    
+    /** Signature signing parameters. */
+    @Nullable private SignatureSigningParameters signatureSigningParameters;
+    
+    /** Signature validation parameters. */
+    @Nullable private SignatureValidationParameters<SignedJWT> signatureValidationParameters;
+    
+    /** Encryption parameters. */
+    @Nullable private EncryptionParameters encryptionParameters;
+    
+    /** Decryption parameters. */
+    @Nullable private DecryptionParameters decryptionParameters;
+    
+    /**
+     * Get the parameters to use for XML signature signing operations.
+     * 
+     * @return the parameters
+     */
+    @Nullable public SignatureSigningParameters getSignatureSigningParameters() {
+        return signatureSigningParameters;
+    }
+
+    /**
+     * Set the parameters to use for XML signature signing operations.
+     * 
+     * @param params the parameters
+     * 
+     * @return this context
+     */
+    @Nonnull public JWTSecurityParametersContext setSignatureSigningParameters(
+            @Nullable final SignatureSigningParameters params) {
+        signatureSigningParameters = params;
+        return this;
+    }
+    
+    /**
+     * Get the parameters to use for XML signature validation operations.
+     * 
+     * @return the parameters
+     */
+    @Nullable public SignatureValidationParameters<SignedJWT> getSignatureValidationParameters() {
+        return signatureValidationParameters;
+    }
+
+    /**
+     * Set the parameters to use for XML signature validation operations.
+     * 
+     * @param params The signatureValidationParameters to set
+     * 
+     * @return this context
+     */
+    @Nonnull public JWTSecurityParametersContext setSignatureValidationParameters(
+            @Nullable final SignatureValidationParameters<SignedJWT> params) {
+        signatureValidationParameters = params;
+        return this;
+    }
+    
+    /**
+     * Get the parameters to use for XML encryption operations.
+     * 
+     * @return the parameters
+     */
+    @Nullable public EncryptionParameters getEncryptionParameters() {
+        return encryptionParameters;
+    }
+
+    /**
+     * Set the parameters to use for XML encryption operations.
+     * 
+     * @param params the parameters
+     * 
+     * @return this context
+     */
+    @Nonnull public JWTSecurityParametersContext setEncryptionParameters(@Nullable final EncryptionParameters params) {
+        encryptionParameters = params;
+        return this;
+    }
+
+    /**
+     * Get the parameters to use for XML decryption operations.
+     * 
+     * @return the parameters
+     */
+    @Nullable public DecryptionParameters getDecryptionParameters() {
+        return decryptionParameters;
+    }
+
+    /**
+     * Set the parameters to use for XML decryption operations.
+     * 
+     * @param params the parameters
+     * 
+     * @return this context
+     */
+    @Nonnull public JWTSecurityParametersContext setDecryptionParameters(@Nullable final DecryptionParameters params) {
+        decryptionParameters = params;
+        return this;
+    }
+
+}
diff --git a/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/criterion/JWTSignatureValidationConfigurationCriterion.java b/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/criterion/JWTSignatureValidationConfigurationCriterion.java
new file mode 100644
index 0000000..8e8c34a
--- /dev/null
+++ b/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/criterion/JWTSignatureValidationConfigurationCriterion.java
@@ -0,0 +1,109 @@
+/*
+ * 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.oidc.security.criterion;
+
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+import com.nimbusds.jwt.SignedJWT;
+
+import net.shibboleth.oidc.security.SignatureValidationConfiguration;
+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.NotLive;
+import net.shibboleth.utilities.java.support.annotation.constraint.Unmodifiable;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.utilities.java.support.resolver.Criterion;
+
+/**
+ * Criterion which holds one or more instances of {@link SignatureValidationConfiguration} 
+ * for {@link SignedJWT signed JWTs}.
+ */
+public class JWTSignatureValidationConfigurationCriterion implements Criterion {
+    
+    /** The list of configuration instances. */
+    @Nonnull @NonnullElements private final List<SignatureValidationConfiguration<SignedJWT>> configs;
+    
+    /**
+     * Constructor.
+     *
+     * @param configurations list of configuration instances
+     */
+    public JWTSignatureValidationConfigurationCriterion(@Nonnull @NonnullElements @NotEmpty final
+            List<SignatureValidationConfiguration<SignedJWT>> configurations) {
+        configs = List.copyOf(Constraint.isNotNull(configurations, "List of configurations cannot be null"));
+        Constraint.isNotEmpty(configs, "At least one configuration is required");
+        
+    }
+    
+    /**
+     * Constructor.
+     *
+     * @param configurations varargs array of configuration instances
+     */
+    public JWTSignatureValidationConfigurationCriterion(@Nonnull @NonnullElements @NotEmpty final
+            SignatureValidationConfiguration<SignedJWT>... configurations) {
+        configs = List.of(Constraint.isNotNull(configurations, "List of configurations cannot be null"));
+        Constraint.isNotEmpty(configs, "At least one configuration is required");
+    }
+    
+    /**
+     * Get the list of configuration instances.
+     * @return the list of configuration instances
+     */
+    @Nonnull @NonnullElements @NotLive @Unmodifiable @NotEmpty
+    public List<SignatureValidationConfiguration<SignedJWT>> getConfigurations() {
+        return configs;
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        final StringBuilder builder = new StringBuilder();
+        builder.append("GenericSignatureValidationConfigurationCriterion [configs=");
+        builder.append(configs);
+        builder.append("]");
+        return builder.toString();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return configs.hashCode();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+
+        if (obj == null) {
+            return false;
+        }
+        if (obj instanceof JWTSignatureValidationConfigurationCriterion) {
+            return configs.equals(((JWTSignatureValidationConfigurationCriterion) obj).getConfigurations());
+        }
+
+        return false;
+    }
+
+}
+
diff --git a/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/criterion/KeyIdCriterion.java b/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/criterion/KeyIdCriterion.java
new file mode 100644
index 0000000..a071373
--- /dev/null
+++ b/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/criterion/KeyIdCriterion.java
@@ -0,0 +1,82 @@
+package net.shibboleth.oidc.security.criterion;
+
+import javax.annotation.Nonnull;
+
+import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.utilities.java.support.primitive.StringSupport;
+import net.shibboleth.utilities.java.support.resolver.Criterion;
+
+
+/**
+ * An implementation of {@link Criterion} which specifies key ID criteria.
+ */
+public class KeyIdCriterion implements Criterion {
+
+    /** Key ID of resolved credentials.  */
+    private String keyId;
+    
+    /**
+     * Constructor.
+     *
+     * @param id key identifier.
+     */
+    public KeyIdCriterion(@Nonnull final String id) {
+        setKeyId(id);
+    }
+
+    /**
+     * Get the key ID criteria.
+     * 
+     * @return Returns the keyId.
+     */
+    @Nonnull public String getKeyId() {
+        return keyId;
+    }
+
+    /**
+     * Set the key name criteria.
+     * 
+     * @param id The keyId to set.
+     */
+    public void setKeyId(@Nonnull final String id) {
+        final String trimmed = StringSupport.trimOrNull(id);
+        Constraint.isNotNull(trimmed, "Key id criteria value cannot be null or empty");
+
+        keyId = trimmed;
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        final StringBuilder builder = new StringBuilder();
+        builder.append("KeyIdCriterion [keyId =");
+        builder.append(keyId);
+        builder.append("]");
+        return builder.toString();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return keyId.hashCode();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+
+        if (obj == null) {
+            return false;
+        }
+
+        if (obj instanceof KeyIdCriterion) {
+            return keyId.equals(((KeyIdCriterion) obj).keyId);
+        }
+
+        return false;
+    }
+
+}
diff --git a/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/criterion/ProviderMetadataCriterion.java b/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/criterion/ProviderMetadataCriterion.java
new file mode 100644
index 0000000..be342bd
--- /dev/null
+++ b/oidc-common-crypto-api/src/main/java/net/shibboleth/oidc/security/criterion/ProviderMetadataCriterion.java
@@ -0,0 +1,69 @@
+package net.shibboleth.oidc.security.criterion;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.saml.criterion.RoleDescriptorCriterion;
+import org.opensaml.saml.saml2.metadata.RoleDescriptor;
+
+import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
+
+import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.utilities.java.support.resolver.Criterion;
+
+public class ProviderMetadataCriterion implements Criterion {
+    
+    /** The OIDC provider metadata. */
+    @Nonnull private final OIDCProviderMetadata metadata;
+    
+    /**
+     * Constructor.
+     * 
+     * @param providerMetadata the provider's metadata
+     */
+    public ProviderMetadataCriterion(@Nonnull final OIDCProviderMetadata providerMetadata) {
+        metadata = Constraint.isNotNull(providerMetadata, "Provider Metadata cannot be null");
+    }
+
+    /**
+     * Gets the OIDC provider metadata.
+     * 
+     * @return the entity role
+     */
+    @Nonnull public OIDCProviderMetadata getMetadata() {
+        return metadata;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        final StringBuilder builder = new StringBuilder();
+        builder.append("ProviderMetadataCriterion [metadata=");
+        builder.append(metadata);
+        builder.append("]");
+        return builder.toString();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return metadata.hashCode();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+
+        if (obj == null) {
+            return false;
+        }
+
+        if (obj instanceof RoleDescriptorCriterion) {
+            return metadata.equals(((ProviderMetadataCriterion) obj).metadata);
+        }
+
+        return false;
+    }
+}
\ No newline at end of file
diff --git a/oidc-common-crypto-impl/pom.xml b/oidc-common-crypto-impl/pom.xml
index 8c1add8..097c258 100644
--- a/oidc-common-crypto-impl/pom.xml
+++ b/oidc-common-crypto-impl/pom.xml
@@ -19,6 +19,12 @@
     </properties>
     
     <dependencies>
+         <!-- Do we need this? should not!! -->
+        <dependency>
+            <groupId>${opensaml.groupId}</groupId>
+            <artifactId>opensaml-xmlsec-impl</artifactId>
+            <scope>provided</scope>
+        </dependency>
         <dependency>
             <groupId>com.nimbusds</groupId>
             <artifactId>nimbus-jose-jwt</artifactId>
diff --git a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/credential/impl/EvaluableKeyIDCredentialCriterion.java b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/credential/impl/EvaluableKeyIDCredentialCriterion.java
new file mode 100644
index 0000000..5b4854d
--- /dev/null
+++ b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/credential/impl/EvaluableKeyIDCredentialCriterion.java
@@ -0,0 +1,98 @@
+package net.shibboleth.oidc.security.credential.impl;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.security.credential.Credential;
+import org.opensaml.security.credential.criteria.impl.EvaluableCredentialCriterion;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.shibboleth.oidc.security.credential.JWKCredential;
+import net.shibboleth.oidc.security.criterion.KeyIdCriterion;
+import net.shibboleth.utilities.java.support.logic.AbstractTriStatePredicate;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.utilities.java.support.primitive.StringSupport;
+
+/**
+ * Instance of evaluable credential criteria for evaluating credential key IDs.
+ */
+public class EvaluableKeyIDCredentialCriterion extends AbstractTriStatePredicate<Credential> 
+                implements EvaluableCredentialCriterion {
+    
+    /** Logger. */
+    private final Logger log = LoggerFactory.getLogger(EvaluableKeyIDCredentialCriterion.class);
+    
+    /** Base criteria. */
+    private final String keyId;
+    
+    /**
+    * Constructor.
+    *
+    * @param criteria the criteria which is the basis for evaluation
+    */
+   public EvaluableKeyIDCredentialCriterion(@Nonnull final KeyIdCriterion criteria) {
+       keyId = Constraint.isNotNull(criteria, "Criterion instance cannot be null").getKeyId();
+   }
+   
+   /**
+    * Constructor.
+    *
+    * @param newKeyId the criteria value which is the basis for evaluation
+    */
+   public EvaluableKeyIDCredentialCriterion(@Nonnull final String newKeyId) {
+       final String trimmed = StringSupport.trimOrNull(newKeyId);
+       Constraint.isNotNull(trimmed, "Key id cannot be null or empty");
+
+       keyId = trimmed;
+   }
+
+    @Override
+    public boolean test(@Nullable final Credential target) {
+        if (target == null) {
+            log.error("Credential target was null");
+            return isNullInputSatisfies();
+        }
+        if (!(target instanceof JWKCredential) || 
+                ((JWKCredential)target).getKid() == null || ((JWKCredential)target).getKid().isEmpty()) {
+            log.info("Could not evaluate criteria, credential contained no key ID");
+            return isUnevaluableSatisfies();
+        }
+        return ((JWKCredential)target).getKid().equals(keyId);
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        final StringBuilder builder = new StringBuilder();
+        builder.append("EvaluableKeyIDCredentialCriterion [keyId =");
+        builder.append(keyId);
+        builder.append("]");
+        return builder.toString();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return keyId.hashCode();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+
+        if (obj == null) {
+            return false;
+        }
+
+        if (obj instanceof EvaluableKeyIDCredentialCriterion) {
+            return keyId.equals(((EvaluableKeyIDCredentialCriterion) obj).keyId);
+        }
+
+        return false;
+    }
+
+}
diff --git a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/BaseJWTSignatureSecurityHandler.java b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/BaseJWTSignatureSecurityHandler.java
new file mode 100644
index 0000000..cf9fa26
--- /dev/null
+++ b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/BaseJWTSignatureSecurityHandler.java
@@ -0,0 +1,118 @@
+/*
+ * 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.oidc.security.impl;
+
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.messaging.handler.MessageHandlerException;
+import org.opensaml.security.credential.UsageType;
+import org.opensaml.security.criteria.UsageCriterion;
+import org.opensaml.security.messaging.impl.BaseTrustEngineSecurityHandler;
+import org.opensaml.security.trust.TrustEngine;
+
+import com.nimbusds.jwt.SignedJWT;
+import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
+
+import net.shibboleth.oidc.security.context.JWTSecurityParametersContext;
+import net.shibboleth.oidc.security.criterion.ProviderMetadataCriterion;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.utilities.java.support.logic.FunctionSupport;
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+
+public abstract class BaseJWTSignatureSecurityHandler extends BaseTrustEngineSecurityHandler<SignedJWT> {
+    
+    /** 
+     * Function that looks up provider metadata from the given message context. Can return {@literal null}
+     * if not used.
+     */
+    @Nonnull private Function<MessageContext, OIDCProviderMetadata> providerMetadataLookupStrategy;
+    
+    /** The provider metadata found from the lookup strategy.*/
+    @Nullable private OIDCProviderMetadata providerMetadata;
+
+    /** Constructor.*/
+    protected BaseJWTSignatureSecurityHandler() {
+        providerMetadataLookupStrategy = FunctionSupport.constant(null);
+    }
+    
+    /**
+     * Set the lookup strategy to locate the OpenID providers metadata.
+     * 
+     * @param strategy the strategy.
+     */
+    public void setProviderMetadataLookupStrategy(
+            @Nonnull final Function<MessageContext, OIDCProviderMetadata> strategy) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        
+        providerMetadataLookupStrategy = 
+                Constraint.isNotNull(strategy,"Provider metadata lookup strategy can not be null");
+    }
+    
+    @Override
+    @Nullable protected TrustEngine<SignedJWT> resolveTrustEngine(final MessageContext messageContext) {
+        final JWTSecurityParametersContext secParams = 
+                messageContext.getSubcontext(JWTSecurityParametersContext.class);
+        if (secParams == null || secParams.getSignatureValidationParameters() == null) {
+            return null;
+        }
+        return secParams.getSignatureValidationParameters().getSignatureTrustEngine();
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    protected boolean doPreInvoke(@Nonnull final MessageContext messageContext) throws MessageHandlerException {
+        
+        if (!super.doPreInvoke(messageContext)) {
+            return false;
+        }        
+        providerMetadata = providerMetadataLookupStrategy.apply(messageContext);  
+        return true;
+    }
+
+    @Override
+    protected CriteriaSet buildCriteriaSet(final String entityID, final MessageContext messageContext)
+            throws MessageHandlerException {
+        
+        final CriteriaSet criteriaSet = new CriteriaSet();
+        
+        if (providerMetadata != null) {
+            criteriaSet.add(new ProviderMetadataCriterion(providerMetadata));
+        }
+//        if (!Strings.isNullOrEmpty(entityID)) {
+//            criteriaSet.add(new EntityIdCriterion(entityID) );
+//        }
+       
+        criteriaSet.add( new UsageCriterion(UsageType.SIGNING) );
+        
+//        final SignedJWTSecurityParametersContext secParamsContext =
+//                messageContext.getSubcontext(SignedJWTSecurityParametersContext.class);
+//        if (secParamsContext != null && secParamsContext.getSignatureValidationParameters() != null) {
+//            criteriaSet.add(
+//                    new SignatureValidationParametersCriterion(secParamsContext.getSignatureValidationParameters()));
+//        }
+        
+        return criteriaSet;
+    }
+
+
+}
diff --git a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/BaseSignedJWTTrustEngine.java b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/BaseSignedJWTTrustEngine.java
index 2e426ab..e372896 100644
--- a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/BaseSignedJWTTrustEngine.java
+++ b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/BaseSignedJWTTrustEngine.java
@@ -217,10 +217,11 @@ public abstract class BaseSignedJWTTrustEngine<TrustBasisType> implements TrustE
         
         //TODO handle JWK Key Set (jku)?
         
-        List<Credential> credentials = new ArrayList<>();
+        final List<Credential> credentials = new ArrayList<>();
         
         if (signedJWT.getHeader().getJWK() != null) {
-            final Credential cred = buildCredential(signedJWT.getHeader().getJWK());
+            final Credential cred = buildCredential(signedJWT.getHeader().getJWK(),
+                    signedJWT.getHeader().getKeyID());
             if (cred != null) {
                 credentials.add(cred);
             }
@@ -230,7 +231,9 @@ public abstract class BaseSignedJWTTrustEngine<TrustBasisType> implements TrustE
     }
     
     //TODO maybe just temporary in favor of separate components?
-    @Nullable protected BasicJWKCredential buildCredential(@Nonnull final JWK jwk) {
+    @Nullable protected BasicJWKCredential buildCredential(@Nonnull final JWK jwk, 
+            @Nullable final String headerKid) {
+        
         final BasicJWKCredential credential = new BasicJWKCredential();
         if (jwk.getKeyType() == KeyType.EC || jwk.getKeyType() == KeyType.RSA) {
             try {
@@ -247,6 +250,11 @@ public abstract class BaseSignedJWTTrustEngine<TrustBasisType> implements TrustE
             credential.getKeyNames().add(jwk.getKeyID());
             credential.setKid(jwk.getKeyID());
         }
+        if (headerKid != null && !headerKid.equals(credential.getKid())) {
+            log.warn("Key ID in JOSE header does not match 'kid' in JWK");
+            return null;
+            
+        }
 
         if (jwk.getKeyUse() != null) {
             credential.setUsageType(CredentialConversionUtil.getUsageType(jwk));
diff --git a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/BasicSignatureValidationConfiguration.java b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/BasicSignatureValidationConfiguration.java
new file mode 100644
index 0000000..4f668a8
--- /dev/null
+++ b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/BasicSignatureValidationConfiguration.java
@@ -0,0 +1,40 @@
+package net.shibboleth.oidc.security.impl;
+
+import javax.annotation.Nullable;
+
+import org.opensaml.security.trust.TrustEngine;
+import org.opensaml.xmlsec.impl.BasicWhitelistBlacklistConfiguration;
+
+import net.shibboleth.oidc.security.SignatureValidationConfiguration;
+
+
+//TODO same as BasicSignatureValidationConfiguration. Needs to not be generic, but for JWT Trust Engine?
+public class BasicSignatureValidationConfiguration<T> extends BasicWhitelistBlacklistConfiguration 
+                    implements SignatureValidationConfiguration<T> {
+    
+    
+    /** The signature trust engine to use. */
+    @Nullable private TrustEngine<T> signatureTrustEngine;
+    
+    //TODO chaining to parent config instance on getters? or use a wrapping proxy, etc?
+    
+    /**
+     * Get the signature trust engine to use.
+     * 
+     * @return the signature trust engine
+     */
+    @Override
+    @Nullable public TrustEngine<T> getSignatureTrustEngine() {
+        return signatureTrustEngine;
+    }
+
+    /**
+     * Set the signature trust engine to use.
+     * 
+     * @param engine the signature trust engine
+     */
+    public void setSignatureTrustEngine(@Nullable final TrustEngine<T> engine) {
+        signatureTrustEngine = engine;
+    }
+
+}
diff --git a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/ExplicitKeySignedJWTTrustEngine.java b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/ExplicitKeySignedJWTTrustEngine.java
index 79d5366..0f8ca2f 100644
--- a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/ExplicitKeySignedJWTTrustEngine.java
+++ b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/ExplicitKeySignedJWTTrustEngine.java
@@ -1,3 +1,20 @@
+/*
+ * 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.oidc.security.impl;
 
 import javax.annotation.Nonnull;
@@ -16,6 +33,8 @@ import org.slf4j.LoggerFactory;
 import com.google.common.base.Strings;
 import com.nimbusds.jwt.SignedJWT;
 
+import net.shibboleth.oidc.security.credential.impl.EvaluableKeyIDCredentialCriterion;
+import net.shibboleth.oidc.security.criterion.KeyIdCriterion;
 import net.shibboleth.utilities.java.support.annotation.ParameterName;
 import net.shibboleth.utilities.java.support.logic.Constraint;
 import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
@@ -64,6 +83,12 @@ public class ExplicitKeySignedJWTTrustEngine extends BaseSignedJWTTrustEngine<It
         if (!Strings.isNullOrEmpty(jcaAlgorithm)) {
             criteriaSet.add(new KeyAlgorithmCriterion(jcaAlgorithm), true);
         }
+        
+        // Add the kid from the JOSE header if available
+        final String kid = signedJWT.getHeader().getKeyID();
+        if (!Strings.isNullOrEmpty(kid)) {
+            criteriaSet.add(new EvaluableKeyIDCredentialCriterion(new KeyIdCriterion(kid)));
+        }
 
         final Iterable<Credential> trustedCredentials;
         try {
diff --git a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/JWTMessageSignatureSecurityHandler.java b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/JWTMessageSignatureSecurityHandler.java
new file mode 100644
index 0000000..b00e0e5
--- /dev/null
+++ b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/JWTMessageSignatureSecurityHandler.java
@@ -0,0 +1,114 @@
+package net.shibboleth.oidc.security.impl;
+
+import java.text.ParseException;
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.messaging.handler.MessageHandlerException;
+import org.opensaml.security.credential.UsageType;
+import org.opensaml.security.criteria.UsageCriterion;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.nimbusds.jose.JWSObject.State;
+import com.nimbusds.jwt.JWTClaimsSet;
+import com.nimbusds.jwt.SignedJWT;
+
+import net.shibboleth.oidc.security.credential.impl.EvaluableKeyIDCredentialCriterion;
+import net.shibboleth.oidc.security.criterion.KeyIdCriterion;
+import net.shibboleth.oidc.security.criterion.ProviderMetadataCriterion;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+
+public class JWTMessageSignatureSecurityHandler extends BaseJWTSignatureSecurityHandler {
+    
+    /** Logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(JWTMessageSignatureSecurityHandler.class);
+    
+    /** Function that looks up a signed JWT token from the given message context to validate.*/
+    @NonnullAfterInit private Function<MessageContext, SignedJWT> jwtTokenLookupStrategy;
+    
+    /** The extracted signed JWT that is to be validated.*/
+    @Nullable private SignedJWT signedJwt;
+    
+    /** The stashed claims set belonging to the JWT.*/
+    @Nullable private JWTClaimsSet claimsSet;
+    
+    /**
+     * Set the strategy used to look up a {@link SignedJWT signed JWT token}.
+     * 
+     * @param strategy lookup strategy
+     */
+    public void setJwtTokenLookupStrategy(
+            @Nonnull final Function<MessageContext, SignedJWT> strategy) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        
+        jwtTokenLookupStrategy = Constraint.isNotNull(strategy,
+                "JwtToken lookup strategy cannot be null");
+    }
+    
+    @Override
+    protected void doInitialize() throws ComponentInitializationException {
+        super.doInitialize();
+        
+        if (jwtTokenLookupStrategy == null) {
+            throw new ComponentInitializationException("JwtTokenLookupStrategy cannot be null");
+        }
+    }
+    
+    @Override
+    protected boolean doPreInvoke(final MessageContext messageContext) throws MessageHandlerException {  
+        if (!super.doPreInvoke(messageContext)) {
+            return false;
+        }
+        
+        signedJwt = jwtTokenLookupStrategy.apply(messageContext);
+        if (signedJwt == null) {
+            log.debug("{} Extracted JWT was not a SignedJWT, cannot process signature",
+                    getLogPrefix());
+            throw new MessageHandlerException("Signed JWT was missing or unpopulated");
+        }
+        try {
+            //TODO debatable we should fail here if there are no claims, as the signature can still be checked.
+            claimsSet = signedJwt.getJWTClaimsSet();
+        } catch (final ParseException e) {
+            throw new MessageHandlerException("Signed JWT did not have any claims, signature check failed");
+        }
+        
+        return true;
+        
+    }
+
+    @Override
+    protected void doInvoke(final MessageContext messageContext) throws MessageHandlerException {
+        
+        
+        if (signedJwt.getState() != State.SIGNED && signedJwt.getState() != State.VERIFIED) {
+            log.debug("{} The JWS object must be in a signed or verified state, cannot process signature",
+                    getLogPrefix());
+            throw new MessageHandlerException("Validation of JWS failed. JWT is not signed.");
+        }
+        if (signedJwt.getState() == State.VERIFIED) {
+            log.debug("{} The JWS object was already verified! validating again", getLogPrefix());
+        }
+        
+        //TODO entityID could be issuerID?
+        if (evaluate(signedJwt, null, messageContext)) {
+            log.debug("{} Validation of JWS token signature succeeded",
+                    getLogPrefix());
+        } else {
+            log.debug(
+                    "{} Validation of JWS token signature failed for context issuer '{}'",
+                    getLogPrefix(), claimsSet.getIssuer());
+            throw new MessageHandlerException("Validation of JWS failed");
+        }
+        
+    }
+    
+}
diff --git a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/OIDCProviderConfigurationSignatureValidationParametersResolver.java b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/OIDCProviderConfigurationSignatureValidationParametersResolver.java
new file mode 100644
index 0000000..294cf86
--- /dev/null
+++ b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/OIDCProviderConfigurationSignatureValidationParametersResolver.java
@@ -0,0 +1,119 @@
+/*
+ * 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.oidc.security.impl;
+
+import java.util.Collections;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.security.trust.TrustEngine;
+
+import org.opensaml.xmlsec.impl.AbstractSecurityParametersResolver;
+import org.opensaml.xmlsec.signature.support.SignatureTrustEngine;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.nimbusds.jwt.SignedJWT;
+
+import net.shibboleth.oidc.security.SignatureValidationConfiguration;
+import net.shibboleth.oidc.security.SignatureValidationParameters;
+import net.shibboleth.oidc.security.SignatureValidationParametersResolver;
+import net.shibboleth.oidc.security.criterion.JWTSignatureValidationConfigurationCriterion;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+import net.shibboleth.utilities.java.support.resolver.ResolverException;
+
+public class OIDCProviderConfigurationSignatureValidationParametersResolver
+        extends AbstractSecurityParametersResolver<SignatureValidationParameters<SignedJWT>>
+        implements SignatureValidationParametersResolver<SignedJWT> {
+
+    /** Logger. */
+    @Nonnull
+    private final Logger log =
+            LoggerFactory.getLogger(OIDCProviderConfigurationSignatureValidationParametersResolver.class);
+
+    /** {@inheritDoc} */
+    @Override
+    @Nonnull
+    public Iterable<SignatureValidationParameters<SignedJWT>> resolve(@Nonnull final CriteriaSet criteria)
+            throws ResolverException {
+
+        final SignatureValidationParameters<SignedJWT> params = resolveSingle(criteria);
+        if (params != null) {
+            return Collections.singletonList(params);
+        }
+        return Collections.emptyList();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    @Nullable
+    public SignatureValidationParameters<SignedJWT> resolveSingle(@Nonnull final CriteriaSet criteria) throws ResolverException {
+        Constraint.isNotNull(criteria, "CriteriaSet was null");
+        Constraint.isNotNull(criteria.get(JWTSignatureValidationConfigurationCriterion.class),
+                "Resolver requires an instance of SignatureValidationConfigurationCriterion");
+
+        final SignatureValidationParameters<SignedJWT> params = new SignatureValidationParameters<>();
+
+        resolveAndPopulateIncludesExcludes(params, criteria,
+                criteria.get(JWTSignatureValidationConfigurationCriterion.class).getConfigurations());
+
+        params.setSignatureTrustEngine(resolveSignatureTrustEngine(criteria));
+
+        logResult(params);
+
+        return params;
+    }
+
+    /**
+     * Log the resolved parameters.
+     * 
+     * @param params the resolved param
+     */
+    protected void logResult(@Nonnull final SignatureValidationParameters<SignedJWT> params) {
+        if (log.isDebugEnabled()) {
+            log.debug("Resolved JWT SignatureValidationParameters:");
+
+            log.debug("\tAlgorithm includes: {}", params.getIncludedAlgorithms());
+            log.debug("\tAlgorithm excludes: {}", params.getExcludedAlgorithms());
+
+            log.debug("\tSignatureTrustEngine: {}", params.getSignatureTrustEngine() != null ? "present" : "null");
+        }
+    }
+
+    /**
+     * Resolve and return the effective {@link SignatureTrustEngine}.
+     * 
+     * @param criteria the input criteria being evaluated
+     * 
+     * @return the effective resolver, or null
+     */
+    @Nullable
+    protected TrustEngine<SignedJWT> resolveSignatureTrustEngine(@Nonnull final CriteriaSet criteria) {
+
+        for (final SignatureValidationConfiguration<SignedJWT> config : criteria
+                .get(JWTSignatureValidationConfigurationCriterion.class).getConfigurations()) {
+            if (config.getSignatureTrustEngine() != null) {
+                return config.getSignatureTrustEngine();
+            }
+        }
+        return null;
+    }
+
+}
diff --git a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/PopulateJWTSignatureValidationParameters.java b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/PopulateJWTSignatureValidationParameters.java
new file mode 100644
index 0000000..13759a0
--- /dev/null
+++ b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/PopulateJWTSignatureValidationParameters.java
@@ -0,0 +1,142 @@
+/*
+ * 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.oidc.security.impl;
+
+import java.util.List;
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
+import org.opensaml.profile.action.AbstractHandlerDelegatingProfileAction;
+import org.opensaml.profile.action.EventIds;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.profile.context.navigate.InboundMessageContextLookup;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.nimbusds.jwt.SignedJWT;
+
+import net.shibboleth.oidc.security.SignatureValidationConfiguration;
+import net.shibboleth.oidc.security.SignatureValidationParameters;
+import net.shibboleth.oidc.security.SignatureValidationParametersResolver;
+import net.shibboleth.oidc.security.context.JWTSecurityParametersContext;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+
+/**
+ * Action that resolves and populates {@link SignatureValidationParameters} on a {@link JWTSecurityParametersContext}
+ * created/accessed via a lookup function, by default on the inbound message context.
+ * 
+ * @event {@link EventIds#PROCEED_EVENT_ID}
+ * @event {@link EventIds#INVALID_MSG_CTX}
+ * @event {@link EventIds#MESSAGE_PROC_ERROR}
+ */
+public class PopulateJWTSignatureValidationParameters 
+        extends AbstractHandlerDelegatingProfileAction<PopulateJWTSignatureValidationParametersHandler> {
+
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(PopulateJWTSignatureValidationParameters.class);
+    
+    /** Strategy used to look up the {@link JWTSecurityParametersContext} to set the parameters for. */
+    @Nonnull private Function<ProfileRequestContext,JWTSecurityParametersContext> securityParametersContextLookupStrategy;
+    
+    /** Strategy used to lookup a per-request {@link SignatureValidationConfiguration} list. */
+    @NonnullAfterInit
+    private Function<ProfileRequestContext,List<SignatureValidationConfiguration<SignedJWT>>> configurationLookupStrategy;
+    
+    /** Resolver for parameters to store into context. */
+    @NonnullAfterInit private SignatureValidationParametersResolver<SignedJWT> resolver;
+    
+    /**
+     * Constructor.
+     */
+    public PopulateJWTSignatureValidationParameters() {
+        super(PopulateJWTSignatureValidationParametersHandler.class, new InboundMessageContextLookup());
+        
+        // Create context by default.
+        securityParametersContextLookupStrategy =
+                new ChildContextLookup<>(JWTSecurityParametersContext.class, true).compose(
+                        new InboundMessageContextLookup());
+    }
+
+    /**
+     * Set the strategy used to look up the {@link JWTSecurityParametersContext} to set the parameters for.
+     * 
+     * @param strategy lookup strategy
+     */
+    public void setSecurityParametersContextLookupStrategy(
+            @Nonnull final Function<ProfileRequestContext,JWTSecurityParametersContext> strategy) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+        securityParametersContextLookupStrategy = Constraint.isNotNull(strategy,
+                "SecurityParametersContext lookup strategy cannot be null");
+    }
+    
+    /**
+     * Set the strategy used to look up a per-request {@link SignatureValidationConfiguration} list.
+     * 
+     * @param strategy lookup strategy
+     */
+    public void setConfigurationLookupStrategy(
+            @Nonnull final Function<ProfileRequestContext,List<SignatureValidationConfiguration<SignedJWT>>> strategy) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        
+        configurationLookupStrategy = Constraint.isNotNull(strategy,
+                "SignatureValidationConfiguration lookup strategy cannot be null");
+    }
+    
+    /**
+     * Set the resolver to use for the parameters to store into the context.
+     * 
+     * @param newResolver   resolver to use
+     */
+    public void setSignatureValidationParametersResolver(
+            @Nonnull final SignatureValidationParametersResolver<SignedJWT> newResolver) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        
+        resolver = Constraint.isNotNull(newResolver, "SignatureValidationParametersResolver cannot be null");
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    protected void doInitialize() throws ComponentInitializationException {
+        super.doInitialize();
+        
+        if (resolver == null) {
+            throw new ComponentInitializationException("SignatureValidationParametersResolver cannot be null");
+        } 
+//        else if (configurationLookupStrategy == null) {
+//            configurationLookupStrategy = new Function<>() {
+//                public List<SignatureValidationConfiguration<?>> apply(final ProfileRequestContext input) {
+//                    return Collections.singletonList(
+//                            SecurityConfigurationSupport.getGlobalSignatureValidationConfiguration());
+//                }
+//            };
+//        }
+        
+        final PopulateJWTSignatureValidationParametersHandler delegate = getDelegate();
+        delegate.setSignatureValidationParametersResolver(resolver);
+        delegate.setConfigurationLookupStrategy(adapt(configurationLookupStrategy));
+        delegate.setSecurityParametersContextLookupStrategy(adapt(securityParametersContextLookupStrategy));
+        delegate.initialize();
+    }
+    
+}
diff --git a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/PopulateJWTSignatureValidationParametersHandler.java b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/PopulateJWTSignatureValidationParametersHandler.java
new file mode 100644
index 0000000..7384ac9
--- /dev/null
+++ b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/PopulateJWTSignatureValidationParametersHandler.java
@@ -0,0 +1,164 @@
+/*
+ * 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.oidc.security.impl;
+
+import java.util.List;
+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.messaging.handler.AbstractMessageHandler;
+import org.opensaml.messaging.handler.MessageHandlerException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.nimbusds.jwt.SignedJWT;
+
+import net.shibboleth.oidc.security.SignatureValidationConfiguration;
+import net.shibboleth.oidc.security.SignatureValidationParameters;
+import net.shibboleth.oidc.security.SignatureValidationParametersResolver;
+import net.shibboleth.oidc.security.context.JWTSecurityParametersContext;
+import net.shibboleth.oidc.security.criterion.JWTSignatureValidationConfigurationCriterion;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+import net.shibboleth.utilities.java.support.resolver.ResolverException;
+
+
+/**
+ * Handler that resolves and populates {@link SignatureValidationParameters} on a {@link JWTSecurityParametersContext}
+ * created/accessed via a lookup function, by default as an immediate child context of the target
+ * {@link MessageContext}.
+ */
+public class PopulateJWTSignatureValidationParametersHandler extends AbstractMessageHandler {
+
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(PopulateJWTSignatureValidationParametersHandler.class);
+    
+    /** Strategy used to look up the {@link JWTSecurityParametersContext} to set the parameters for. */
+    @Nonnull private Function<MessageContext,JWTSecurityParametersContext> securityParametersContextLookupStrategy;
+    
+    /** Strategy used to lookup a per-request {@link SignatureValidationConfiguration} list. */
+    @NonnullAfterInit
+    private Function<MessageContext,List<SignatureValidationConfiguration<SignedJWT>>> configurationLookupStrategy;
+    
+    /** Resolver for parameters to store into context. */
+    @NonnullAfterInit private SignatureValidationParametersResolver<SignedJWT> resolver;
+    
+    /**
+     * Constructor.
+     */
+    public PopulateJWTSignatureValidationParametersHandler() {
+        // Create context by default.
+        securityParametersContextLookupStrategy = new ChildContextLookup<>(JWTSecurityParametersContext.class, true);
+    }
+
+    /**
+     * Set the strategy used to look up the {@link JWTSecurityParametersContext} to set the parameters for.
+     * 
+     * @param strategy lookup strategy
+     */
+    public void setSecurityParametersContextLookupStrategy(
+            @Nonnull final Function<MessageContext, JWTSecurityParametersContext> strategy) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+        securityParametersContextLookupStrategy = Constraint.isNotNull(strategy,
+                "SecurityParametersContext lookup strategy cannot be null");
+    }
+    
+    /**
+     * Set the strategy used to look up a per-request {@link SignatureValidationConfiguration} list.
+     * 
+     * @param strategy lookup strategy
+     */
+    public void setConfigurationLookupStrategy(
+            @Nonnull final Function<MessageContext,List<SignatureValidationConfiguration<SignedJWT>>> strategy) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        
+        configurationLookupStrategy = Constraint.isNotNull(strategy,
+                "SignatureValidationConfiguration lookup strategy cannot be null");
+    }
+    
+    /**
+     * Set the resolver to use for the parameters to store into the context.
+     * 
+     * @param newResolver   resolver to use
+     */
+    public void setSignatureValidationParametersResolver(
+            @Nonnull final SignatureValidationParametersResolver<SignedJWT> newResolver) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        
+        resolver = Constraint.isNotNull(newResolver, "SignatureValidationParametersResolver cannot be null");
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    protected void doInitialize() throws ComponentInitializationException {
+        super.doInitialize();
+        
+        if (resolver == null) {
+            throw new ComponentInitializationException("SignatureValidationParametersResolver cannot be null");
+        } 
+//        else if (configurationLookupStrategy == null) {
+//            configurationLookupStrategy = new Function<>() {
+//                public List<SignatureValidationConfiguration> apply(final MessageContext input) {
+//                    return Collections.singletonList(
+//                            SecurityConfigurationSupport.getGlobalSignatureValidationConfiguration());
+//                }
+//            };
+//        }
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    protected void doInvoke(@Nonnull final MessageContext messageContext) throws MessageHandlerException {
+
+        log.debug("{} Resolving SignatureValidationParameters for request", getLogPrefix());
+        
+        final List<SignatureValidationConfiguration<SignedJWT>> configs = 
+                configurationLookupStrategy.apply(messageContext);
+        if (configs == null || configs.isEmpty()) {
+            log.error("{} No SignatureValidationConfiguration returned by lookup strategy", getLogPrefix());
+            throw new MessageHandlerException("No SignatureValidationConfiguration returned by lookup strategy");
+        }
+        
+        final JWTSecurityParametersContext paramsCtx =
+                securityParametersContextLookupStrategy.apply(messageContext);
+        if (paramsCtx == null) {
+            log.debug("{} No SecurityParametersContext returned by lookup strategy", getLogPrefix());
+            throw new MessageHandlerException("SecurityParametersContext returned by lookup strategy");
+        }
+        
+        try {
+            final SignatureValidationParameters<SignedJWT> params = resolver.resolveSingle(
+                    new CriteriaSet(new JWTSignatureValidationConfigurationCriterion(configs)));
+            paramsCtx.setSignatureValidationParameters(params);
+            log.debug("{} {} SignatureValidationParameters", getLogPrefix(),
+                    params != null ? "Resolved" : "Failed to resolve");
+        } catch (final ResolverException e) {
+            log.error("{} Error resolving SignatureValidationParameters: {}", getLogPrefix(), e.getMessage());
+            throw new MessageHandlerException("Error resolving SignatureValidationParameters");
+        }
+    }
+    
+}
+
diff --git a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/ProviderMetadataCredentialResolver.java b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/ProviderMetadataCredentialResolver.java
new file mode 100644
index 0000000..b54d450
--- /dev/null
+++ b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/ProviderMetadataCredentialResolver.java
@@ -0,0 +1,180 @@
+package net.shibboleth.oidc.security.impl;
+
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Collection;
+import java.util.LinkedHashSet;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.security.credential.Credential;
+import org.opensaml.security.credential.UsageType;
+import org.opensaml.security.credential.impl.AbstractCriteriaFilteringCredentialResolver;
+import org.opensaml.security.criteria.UsageCriterion;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.nimbusds.jose.JOSEException;
+import com.nimbusds.jose.jwk.AsymmetricJWK;
+import com.nimbusds.jose.jwk.ECKey;
+import com.nimbusds.jose.jwk.JWK;
+import com.nimbusds.jose.jwk.JWKSet;
+import com.nimbusds.jose.jwk.KeyUse;
+import com.nimbusds.jose.jwk.RSAKey;
+import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
+
+import net.shibboleth.oidc.jwk.RemoteJwkSetCache;
+import net.shibboleth.oidc.security.credential.BasicJWKCredential;
+import net.shibboleth.oidc.security.criterion.ProviderMetadataCriterion;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.utilities.java.support.annotation.constraint.Positive;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
+import net.shibboleth.utilities.java.support.component.InitializableComponent;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+import net.shibboleth.utilities.java.support.resolver.ResolverException;
+
+
+//TODO not a prototype? so ensure thread-safety
+//TODO Docs
+//TODO Algorithm filters?
+public class ProviderMetadataCredentialResolver extends AbstractCriteriaFilteringCredentialResolver 
+        implements InitializableComponent {
+    
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(ProviderMetadataCredentialResolver.class);
+    
+    /** Initialization flag. */
+    private boolean isInitialized;
+    
+    /** The cache for remote JWK key sets. */
+    @NonnullAfterInit private RemoteJwkSetCache remoteJwkSetCache;
+    
+    /** The remote key refresh interval. Default value: 30 minutes. */
+    @Positive
+    private Duration keyFetchInterval = Duration.ofMinutes(30);
+    
+    /** {@inheritDoc} */
+    @Override
+    public boolean isInitialized() {
+        return isInitialized;
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    public void initialize() throws ComponentInitializationException {
+        
+        if (remoteJwkSetCache == null) {
+            throw new ComponentInitializationException("Remote JWK Set Cache can not be null");
+        }
+        isInitialized = true;
+    }
+    
+    /**
+     * Set the remote key refresh interval.
+     * 
+     * @param interval What to set.
+     */
+    public void setKeyFetchInterval(@Positive final Duration interval) {
+        Constraint.isFalse(interval == null || interval.isNegative(), "Remote key refresh must be greater than 0");
+        keyFetchInterval = interval;
+    }
+    
+    /**
+     * Set the cache for remote JWK key sets.
+     * 
+     * @param jwkSetCache What to set.
+     */
+    public void setRemoteJwkSetCache(final RemoteJwkSetCache jwkSetCache) {
+        remoteJwkSetCache = Constraint.isNotNull(jwkSetCache, "The remote JWK set cache cannot be null");
+    }
+
+    @Override
+    protected Iterable<Credential> resolveFromSource(@Nonnull final CriteriaSet criteriaSet) throws ResolverException {
+        
+        ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
+        Constraint.isNotNull(criteriaSet, "CriteriaSet was null");
+        
+        final UsageType usage = getEffectiveUsageInput(criteriaSet);
+        if (criteriaSet.contains(ProviderMetadataCriterion.class)) {
+            final OIDCProviderMetadata metadata = criteriaSet.get(ProviderMetadataCriterion.class).getMetadata();
+            return resolveFromMetadata(criteriaSet, usage, metadata);
+            
+        } else {
+            throw new ResolverException("Criteria did not contain a ProviderMetadataCriterion " 
+                    + "could not perform resolution");
+        }
+    }
+    
+    @Nonnull protected Collection<Credential> resolveFromMetadata(@Nonnull final CriteriaSet criteriaSet, 
+            @Nonnull final UsageType usage, @Nonnull final OIDCProviderMetadata metadata) {
+        
+        final LinkedHashSet<Credential> credentials = new LinkedHashSet<>(1);
+        
+        // TODO: Need something like this final List<String> algorithms = 
+        // getEffectiveSignatureAlgorithms(criteria, whitelistBlacklistPredicate);
+        if (metadata.getJWKSetURI() != null) {
+            final JWKSet keySet = remoteJwkSetCache.fetch(metadata.getJWKSetURI(),
+                    Instant.now().plus(keyFetchInterval));
+            
+            for (final JWK key : keySet.getKeys()) {
+                if (!matchUsage(key.getKeyUse(), usage)) {
+                    continue;
+                }
+                // Check against supported algo's here? although OP should only have keys for algo's it supports?
+                if (key instanceof RSAKey || key instanceof ECKey) {
+                    final BasicJWKCredential jwkCredential = new BasicJWKCredential();
+                    jwkCredential.setAlgorithm(key.getAlgorithm());
+                    jwkCredential.setKid(key.getKeyID());
+                    jwkCredential.getKeyNames().add(key.getKeyID());
+                    try {
+                        jwkCredential.setPublicKey(((AsymmetricJWK) key).toPublicKey());
+                    } catch (final JOSEException e) {
+                        log.warn("Unable to parse key from keyset");
+                        continue;
+                    }
+                    log.debug("Selected key '{}', of type '{}', for alg '{}'", 
+                            key.getKeyID(), key.getKeyType(), key.getAlgorithm());
+                    credentials.add(jwkCredential);
+                }                
+            }            
+        } else {
+            log.debug("No JWK Set available, no credentials returned");
+        }
+        return credentials;
+   
+    }
+    
+    /**
+     * Match the JWK key usage matches that in the criteria set.
+     * 
+     * @param metadataUsage the value from the 'use' parameter of the JWK.
+     * @param criteriaUsage the value from specified criteria
+     * @return true if the two usage specifiers match for purposes of resolving validation information, false otherwise
+     */
+    protected boolean matchUsage(@Nonnull final KeyUse metadataUsage, @Nonnull final UsageType criteriaUsage) {
+        if (KeyUse.SIGNATURE.equals(metadataUsage) && criteriaUsage == UsageType.SIGNING) {
+            return true;
+        } else if (KeyUse.ENCRYPTION.equals(metadataUsage) && criteriaUsage == UsageType.ENCRYPTION) {
+            return true;
+        }
+        //TODO support unspecified in some way?
+        return false;
+    }
+
+    /**
+     * Get the effective {@link UsageType} input to use.
+     * 
+     * @param criteriaSet the criteria set being processed
+     * @return the effective usage value
+     */
+    @Nonnull protected UsageType getEffectiveUsageInput(@Nonnull final CriteriaSet criteriaSet) {
+        final UsageCriterion usageCriteria = criteriaSet.get(UsageCriterion.class);
+        if (usageCriteria != null) {
+            return usageCriteria.getUsage();
+        }
+        return UsageType.UNSPECIFIED; 
+    }
+
+}
diff --git a/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/impl/ExplicitKeySignedJWTTrustEngineTest.java b/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/impl/ExplicitKeySignedJWTTrustEngineTest.java
new file mode 100644
index 0000000..18f6dd9
--- /dev/null
+++ b/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/impl/ExplicitKeySignedJWTTrustEngineTest.java
@@ -0,0 +1,301 @@
+
+package net.shibboleth.oidc.security.impl;
+
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Collections;
+import java.util.List;
+
+import org.opensaml.security.SecurityException;
+import org.opensaml.security.credential.Credential;
+import org.opensaml.security.credential.CredentialResolver;
+import org.opensaml.security.credential.UsageType;
+import org.opensaml.security.criteria.UsageCriterion;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.nimbusds.jose.JOSEException;
+import com.nimbusds.jose.JOSEObjectType;
+import com.nimbusds.jose.JWSAlgorithm;
+import com.nimbusds.jose.JWSHeader;
+import com.nimbusds.jose.crypto.ECDSASigner;
+import com.nimbusds.jose.jwk.AsymmetricJWK;
+import com.nimbusds.jose.jwk.Curve;
+import com.nimbusds.jose.jwk.ECKey;
+import com.nimbusds.jose.jwk.gen.ECKeyGenerator;
+import com.nimbusds.jwt.JWTClaimsSet;
+import com.nimbusds.jwt.SignedJWT;
+
+import net.shibboleth.oidc.security.credential.BasicJWKCredential;
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+import net.shibboleth.utilities.java.support.resolver.ResolverException;
+
+public class ExplicitKeySignedJWTTrustEngineTest {
+
+    private ExplicitKeySignedJWTTrustEngine engine;
+    
+    private ECKey key;
+    
+    private CredentialResolver credResolver;
+    
+    @BeforeMethod
+    public void setup() throws JOSEException {
+        key = new ECKeyGenerator(Curve.P_256).keyID("123").generate();
+
+        credResolver = new CredentialResolver() {
+
+            @Override
+            public Credential resolveSingle(final CriteriaSet criteria) throws ResolverException {
+                final BasicJWKCredential jwkCredential = new BasicJWKCredential();
+                jwkCredential.setAlgorithm(key.getAlgorithm());
+                jwkCredential.setKid(key.getKeyID());
+                try {
+                    jwkCredential.setPublicKey(((AsymmetricJWK) key).toPublicKey());
+                } catch (final JOSEException e) {                    
+                    fail();
+                }
+                return jwkCredential;
+            }
+
+            @Override
+            public Iterable<Credential> resolve(final CriteriaSet criteria) throws ResolverException {
+                return List.of(resolveSingle(criteria));
+            }
+        };
+        engine = new ExplicitKeySignedJWTTrustEngine(credResolver);
+    }
+
+    @Test
+    public void testSuccess_WithTrustedCredential() throws JOSEException, SecurityException {
+        
+        final CriteriaSet criteria = new CriteriaSet();
+        criteria.add(new UsageCriterion(UsageType.SIGNING));
+        final var valid = engine.validate(createECSignedJWT(key, key.getKeyID(), JWSAlgorithm.ES256, 
+                "https://op.example.com/", "https://rp.example.com"),
+                criteria);
+        assertTrue(valid);
+    }
+    
+    @Test
+    public void testSuccess_WithInlineJWK() throws JOSEException, SecurityException {
+        
+        final CriteriaSet criteria = new CriteriaSet();
+        criteria.add(new UsageCriterion(UsageType.SIGNING));
+        final var valid = engine.validate(createECSignedJWTWithInlineJWK(key, key.getKeyID(), JWSAlgorithm.ES256, 
+                "https://op.example.com/", "https://rp.example.com"),
+                criteria);
+        assertTrue(valid);
+    }
+    
+    @Test
+    public void testSuccess_WithInlineJKU() throws JOSEException, SecurityException, URISyntaxException { 
+        
+        // Create a new resolver which does not resolve any creds, the JKU ones need to be resolved.
+        credResolver = new CredentialResolver() {
+
+            @Override
+            public Credential resolveSingle(final CriteriaSet criteria) throws ResolverException {
+                return null;
+            }
+
+            @Override
+            public Iterable<Credential> resolve(final CriteriaSet criteria) throws ResolverException {
+                return Collections.emptyList();
+            }
+        };
+        engine = new ExplicitKeySignedJWTTrustEngine(credResolver);
+        
+       
+        final CriteriaSet criteria = new CriteriaSet();
+        criteria.add(new UsageCriterion(UsageType.SIGNING));
+        final var valid = engine.validate(createECSignedJWTWithJKU(key, key.getKeyID(), 
+                new URI("https://op.example.com/keys"), JWSAlgorithm.ES256, 
+                "https://op.example.com/", "https://rp.example.com"),
+                criteria);
+        assertTrue(valid);
+    }
+    
+    @Test
+    public void testFailure_InlineJWKWrongKid() throws JOSEException, SecurityException {
+        
+        // Create a new resolver which does not resolve any creds, the kid JOSE headers are different
+        // than the one in the JWK, and should fail.
+        credResolver = new CredentialResolver() {
+
+            @Override
+            public Credential resolveSingle(final CriteriaSet criteria) throws ResolverException {
+                return null;
+            }
+
+            @Override
+            public Iterable<Credential> resolve(final CriteriaSet criteria) throws ResolverException {
+                return Collections.emptyList();
+            }
+        };
+        engine = new ExplicitKeySignedJWTTrustEngine(credResolver);
+        
+        final CriteriaSet criteria = new CriteriaSet();
+        criteria.add(new UsageCriterion(UsageType.SIGNING));
+        final var valid = engine.validate(createECSignedJWTWithInlineJWK(key, "WRONG-KID", JWSAlgorithm.ES256, 
+                "https://op.example.com/", "https://rp.example.com"),
+                criteria);
+        assertFalse(valid);
+    }
+    
+    @Test
+    public void testFailure_InlineJWKInvalidSignature() throws JOSEException, SecurityException {
+        
+        final CriteriaSet criteria = new CriteriaSet();
+        criteria.add(new UsageCriterion(UsageType.SIGNING));
+        final var valid = engine.validate(createECSignedJWTWithDifferentInlineJWK(key, key.getKeyID(),JWSAlgorithm.ES256,
+                // A new signing key, different to the one described in the header
+                new ECKeyGenerator(Curve.P_256).keyID("new").generate(),
+                "https://op.example.com/", "https://rp.example.com"),
+                criteria);
+        assertFalse(valid);
+    }
+    
+
+    
+    /**
+     * Create a JWS without a JKU or inline JWK.
+     * 
+     * @param key the key to sign the JWT.
+     * @param keyId the keyId to describe the key to use in the header.
+     * @param algo the key algorithm.
+     * @param issuer the issuer.
+     * @param audience the audience.
+     * @return the signed JWT
+     * @throws JOSEException on error.
+     */
+    private SignedJWT createECSignedJWT(final ECKey key, final String keyId, 
+            final JWSAlgorithm algo, final String issuer,
+            final String audience) throws JOSEException {
+
+        final var header = new JWSHeader.Builder(algo)
+                .type(JOSEObjectType.JWT)
+                .keyID(keyId)
+                .build();
+        final var payload = new JWTClaimsSet.Builder()
+                .issuer(issuer)
+                .audience(audience)
+                .subject("jdoe")
+                .claim("preferred_username", "jdoe")
+                .claim("name", "J Doe")
+                .build();
+
+        final var signedJWT = new SignedJWT(header, payload);
+        signedJWT.sign(new ECDSASigner(key.toECPrivateKey()));
+        return signedJWT;
+    }
+    
+    
+    /**
+     * Create a JWS with a JKU URL. 
+     * 
+     * @param key the key to sign the JWT.
+     * @param keyId the keyId to describe the key to use in the header.
+     * @param jku the JKU.
+     * @param algo the key algorithm.
+     * @param issuer the issuer.
+     * @param audience the audience.
+     * @return the signed JWT
+     * @throws JOSEException on error.
+     */
+    private SignedJWT createECSignedJWTWithJKU(final ECKey key, final String keyId, 
+            final URI jku, final JWSAlgorithm algo, final String issuer,
+            final String audience) throws JOSEException, URISyntaxException {
+
+        final var header = new JWSHeader.Builder(algo)
+                .type(JOSEObjectType.JWT)
+                .keyID(keyId)
+                .jwkURL(jku)
+                .build();
+        final var payload = new JWTClaimsSet.Builder()
+                .issuer(issuer)
+                .audience(audience)
+                .subject("jdoe")
+                .claim("preferred_username", "jdoe")
+                .claim("name", "J Doe")
+                .build();
+
+        final var signedJWT = new SignedJWT(header, payload);
+        signedJWT.sign(new ECDSASigner(key.toECPrivateKey()));
+        return signedJWT;
+    }
+    
+    /**
+     * Create a JWS with an inline (in JOSE headers) JWK.
+     * 
+     * @param key the key to sign the JWT.
+     * @param keyId the keyId to describe the key to use in the header.
+     * @param algo the key algorithm.
+     * @param issuer the issuer.
+     * @param audience the audience.
+     * @return the signed JWT
+     * @throws JOSEException on error.
+     */
+    private SignedJWT createECSignedJWTWithInlineJWK(final ECKey key,  final String keyId,
+            final JWSAlgorithm algo, final String issuer,
+            final String audience) throws JOSEException {
+
+        final var header = new JWSHeader.Builder(algo)
+                .type(JOSEObjectType.JWT)
+                .keyID(keyId)
+                .jwk(key)
+                .build();
+        final var payload = new JWTClaimsSet.Builder()
+                .issuer(issuer)
+                .audience(audience)
+                .subject("jdoe")
+                .claim("preferred_username", "jdoe")
+                .claim("name", "J Doe")
+                .build();
+
+        final var signedJWT = new SignedJWT(header, payload);
+        signedJWT.sign(new ECDSASigner(key.toECPrivateKey()));
+        return signedJWT;
+    }
+    
+    /**
+     * Create a JWS using a a key that is different to that in the JOSE header.
+     * 
+     * @param key the key to described in the JWK header.
+     * @param keyId the keyId to describe the key to use in the header.
+     * @param algo the key algorithm.
+     * @param signingKey the key used to sign the JWS, which could be different than the key
+     *                  described in the JWK.
+     * @param issuer the issuer.
+     * @param audience the audience.
+     * @return the signed JWT
+     * @throws JOSEException on error.
+     */
+    private SignedJWT createECSignedJWTWithDifferentInlineJWK(final ECKey key, final String keyId,
+            final JWSAlgorithm algo, final ECKey signingKey, final String issuer,
+            final String audience) throws JOSEException {
+
+        final var header = new JWSHeader.Builder(algo)
+                .type(JOSEObjectType.JWT)
+                .keyID(keyId)
+                .jwk(key)
+                .build();
+        final var payload = new JWTClaimsSet.Builder()
+                .issuer(issuer)
+                .audience(audience)
+                .subject("jdoe")
+                .claim("preferred_username", "jdoe")
+                .claim("name", "J Doe")
+                .build();
+
+        final var signedJWT = new SignedJWT(header, payload);
+        signedJWT.sign(new ECDSASigner(signingKey.toECPrivateKey()));
+        return signedJWT;
+    }
+    
+    
+
+}
diff --git a/oidc-common-profile-api/src/main/java/net/shibboleth/oidc/profile/config/OIDCAuthorizationConfiguration.java b/oidc-common-profile-api/src/main/java/net/shibboleth/oidc/profile/config/OIDCAuthorizationConfiguration.java
index fb7ba9d..fc3ab2f 100644
--- a/oidc-common-profile-api/src/main/java/net/shibboleth/oidc/profile/config/OIDCAuthorizationConfiguration.java
+++ b/oidc-common-profile-api/src/main/java/net/shibboleth/oidc/profile/config/OIDCAuthorizationConfiguration.java
@@ -32,6 +32,7 @@ import javax.annotation.Nullable;
 import org.opensaml.profile.context.ProfileRequestContext;
 
 import com.google.common.base.Predicates;
+import com.nimbusds.oauth2.sdk.auth.ClientAuthentication;
 
 import net.shibboleth.idp.authn.config.AuthenticationProfileConfiguration;
 import net.shibboleth.idp.profile.config.OverriddenIssuerProfileConfiguration;
@@ -46,7 +47,7 @@ import net.shibboleth.utilities.java.support.primitive.StringSupport;
 
 /**
  * Profile configuration for the OpenID Connect authorization endpoint.
- * 
+ *
  * <p>It is also usable as a Token endpoint configuration if no non-OIDC use cases are needed.</p>
  */
 public class OIDCAuthorizationConfiguration extends AbstractOIDCSSOConfiguration
@@ -54,7 +55,7 @@ public class OIDCAuthorizationConfiguration extends AbstractOIDCSSOConfiguration
 
     /** ID for this profile configuration. */
     @Nonnull @NotEmpty public static final String PROFILE_ID = "http://shibboleth.net/ns/profiles/oidc/sso/browser";
-    
+
     /** Whether all acr claim requests should be treated as Essential. */
     @Nonnull private Predicate<ProfileRequestContext> acrRequestAlwaysEssentialPredicate;
 
@@ -63,42 +64,59 @@ public class OIDCAuthorizationConfiguration extends AbstractOIDCSSOConfiguration
 
     /** Lookup function to supply lifetime of authz code. */
     @Nonnull private Function<ProfileRequestContext,Duration> authorizeCodeLifetimeLookupStrategy;
-    
+
     /** Lookup function to supply attribute IDs to embed in authorization code or access token. */
     @Nonnull private Function<ProfileRequestContext,Set<String>> encodedAttributesLookupStrategy;
-    
+
     /** Whether to encode authentication request parameters inside a JWT request object .*/
     @Nonnull private Predicate<ProfileRequestContext> useRequestObjectPredicate;
 
     /** Lookup function to supply attribute IDs to omit from UserInfo token. */
     @Nonnull private Function<ProfileRequestContext,Set<String>> deniedUserInfoAttributesLookupStrategy;
-    
+
     /** Whether to include iss parameter in the authentication response. */
     @Nonnull private Predicate<ProfileRequestContext> includeIssuerInResponsePredicate;
 
     /** Enumeration of the HTTP methods used in OIDC authentication requests.*/
-    public enum OIDCHttpRequestMethod {    
+    public enum OIDCHttpRequestMethod {
         /**
          * HTTP GET.
          */
-        GET,                
+        GET,
         /**
          * HTTP POST.
          */
         POST
     }
-    
-    /** 
-     * Which HTTP method should be used to issue OIDC authentication requests. 
-     * Supported values are POST and GET. The default is GET. 
+
+    /**
+     * Which HTTP method should be used to issue OIDC authentication requests.
+     * Supported values are POST and GET. The default is GET.
      */
     @Nonnull private Function<ProfileRequestContext,String> httpRequestMethodLookupStrategy;
 
-    /** Lookup function to supply strategy bi-function for manipulating authorization code claims set. */ 
+    /** Lookup function to supply strategy bi-function for manipulating authorization code claims set. */
     @Nonnull
     private Function<ProfileRequestContext,BiFunction<ProfileRequestContext,Map<String,Object>,Map<String,Object>>>
         authorizationCodeClaimsSetManipulationStrategyLookupStrategy;
 
+    /** Whether to make a UserInfo Endpoint request for End-User claims.*/
+    @Nonnull private Predicate<ProfileRequestContext> retrieveUserInfoEndpointClaims;
+
+    /** An override to specify a specific redirect_uri to use over the normally computed one.*/
+    @Nonnull private Function<ProfileRequestContext, String> redirectUriOverrideLookupStrategy;
+
+    //TODO I think we need a new config class for these 'proxy' attributes
+    // because the switch of RP to OP is confusing otherwise
+    /**
+     * Lookup function to retrieve the client_id from the given profile request context.
+     * Typically obtained from the issuer_id of the downstream relying-party (OP).
+     */
+    @Nonnull private Function<ProfileRequestContext, String> clientIdLookupStrategy;
+
+    /** Lookup function to determine the client_authentication method.  */
+    @Nonnull private Function<ProfileRequestContext, ClientAuthentication> clientAuthenticationLookupStrategy;
+
     /**
      * Constructor.
      */
@@ -117,23 +135,113 @@ public class OIDCAuthorizationConfiguration extends AbstractOIDCSSOConfiguration
         acrRequestAlwaysEssentialPredicate = Predicates.alwaysFalse();
         encodeConsentInTokensPredicate = Predicates.alwaysFalse();
         useRequestObjectPredicate = Predicates.alwaysFalse();
+        retrieveUserInfoEndpointClaims = Predicates.alwaysTrue();
 
         authorizeCodeLifetimeLookupStrategy = FunctionSupport.constant(Duration.ofMinutes(5));
-        
+
         encodedAttributesLookupStrategy = FunctionSupport.constant(null);
         deniedUserInfoAttributesLookupStrategy = FunctionSupport.constant(null);
-        httpRequestMethodLookupStrategy = FunctionSupport.constant(OIDCHttpRequestMethod.GET.toString());
+        redirectUriOverrideLookupStrategy = FunctionSupport.constant(null);
+        clientIdLookupStrategy = FunctionSupport.constant(null);
+        clientAuthenticationLookupStrategy = FunctionSupport.constant(null);
+        httpRequestMethodLookupStrategy = FunctionSupport.constant(OIDCHttpRequestMethod.GET);
 
         includeIssuerInResponsePredicate = Predicates.alwaysFalse();
-        
+
         authorizationCodeClaimsSetManipulationStrategyLookupStrategy = FunctionSupport.constant(null);
     }
 
+    /**
+     * Set the strategy to locate a client_id.
+     *
+     * @param strategy the strategy to use
+     */
+    public void setClientIdLookupStrategy(@Nonnull final Function<ProfileRequestContext, String> strategy) {
+        clientIdLookupStrategy = Constraint.isNotNull(strategy, "Client ID lookup strategy can not be null");
+    }
+
+    /**
+     * Set a fixed client_id to use no matter what the context/request.
+     *
+     * @param clientId the static clientId to use
+     */
+    public void setClientId(@Nullable final String clientId) {
+        clientIdLookupStrategy = FunctionSupport.constant(clientId);
+    }
+
+    /**
+     * Set the client authentication lookup strategy which will locate the client_authentication method
+     * for a given client.
+     *
+     * @param strategy the strategy to use
+     */
+    public void setClientAuthenticationLookupStrategy(
+            @Nonnull final Function<ProfileRequestContext, ClientAuthentication> strategy) {
+        clientAuthenticationLookupStrategy =
+                Constraint.isNotNull(strategy, "Client authentication strategy can not be null");
+    }
+
+    /**
+     * Get the client_authentication for the given context. Typically that associated with the
+     * current client_id.
+     *
+     * @param profileRequestContext the profile request context
+     *
+     * @return the client_authentication method
+     */
+    public ClientAuthentication getClientAuthentication(
+            @Nullable final ProfileRequestContext profileRequestContext) {
+        return clientAuthenticationLookupStrategy.apply(profileRequestContext);
+    }
+
+    /**
+     * Get the client_id appropriate for the given context. Typically that associated
+     * with the chosen downstream OpenID Connect Provider.
+     *
+     * @param profileRequestContext the profile request context
+     *
+     * @return the client_id
+     */
+    public String getClientId(@Nullable final ProfileRequestContext profileRequestContext) {
+        return clientIdLookupStrategy.apply(profileRequestContext);
+    }
+
+    /**
+     * Set the redirect_uri lookup strategy to use to locate an override redirect_rui.
+     *
+     * @param strategy the strategy to use.
+     */
+    public void setRedirectUriOverrideLookupStrategy(@Nonnull final
+            Function<ProfileRequestContext, String> strategy) {
+        redirectUriOverrideLookupStrategy = Constraint.isNotNull(strategy, "Lookup strategy can not be null");
+    }
+
+    /**
+     * Set the override redirect_uri.
+     *
+     * @param uri the redirect_uri
+     */
+    public void setRedirectUriOverride(@Nonnull @NotEmpty final String uri) {
+        redirectUriOverrideLookupStrategy = FunctionSupport.constant(
+                Constraint.isNotNull(uri, "Redirect_uri can not be null or empty"));
+    }
+
+    /**
+     * Get the redirect_uri override if set.
+     *
+     * @param profileRequestContext profile request context
+     *
+     * @return the redirect_uri override
+     */
+    public String getRedirectUriOverride(@Nullable final ProfileRequestContext profileRequestContext) {
+        return redirectUriOverrideLookupStrategy.apply(profileRequestContext);
+    }
+
     /**
      * Get whether all acr claim requests should be treated as Essential.
-     * 
+     *
      * @param profileRequestContext profile request context
-     * 
+     *
      * @return whether all acr claim requests should be treated as Essential
      */
     public boolean isAcrRequestAlwaysEssential(@Nullable final ProfileRequestContext profileRequestContext) {
@@ -142,35 +250,35 @@ public class OIDCAuthorizationConfiguration extends AbstractOIDCSSOConfiguration
 
     /**
      * Set whether all acr claim requests should be treated as Essential.
-     * 
+     *
      * @param flag flag to set
      */
     public void setAcrRequestAlwaysEssential(final boolean flag) {
         acrRequestAlwaysEssentialPredicate = flag ? Predicates.alwaysTrue() : Predicates.alwaysFalse();
     }
-    
+
     /**
      * Set condition for whether all acr claim requests should be treated as Essential.
-     * 
+     *
      * @param condition condition to set
      */
     public void setAcrRequestAlwaysEssentialPredicate(@Nonnull final Predicate<ProfileRequestContext> condition) {
         acrRequestAlwaysEssentialPredicate = Constraint.isNotNull(condition, "Condition cannot be null");
     }
-    
+
     /**
      * Should authentication request parameters should be passed in a single, self contained, JWT?
-     * 
+     *
      * @param profileRequestContext the profile request context
-     * 
+     *
      * @return whether authentication request parameters should be passed in a single, self contained, JWT
      */
     public boolean isUseRequestObject(@Nullable final ProfileRequestContext profileRequestContext) {
         return useRequestObjectPredicate.test(profileRequestContext);
     }
-    
+
     /**
-     * Set whether the authentication request parameters should be passed in a single, self contained, JWT. 
+     * Set whether the authentication request parameters should be passed in a single, self contained, JWT.
      *
      * @param flag flag to set
      */
@@ -179,9 +287,9 @@ public class OIDCAuthorizationConfiguration extends AbstractOIDCSSOConfiguration
     }
 
     /**
-     * Set condition for whether the authentication request parameters should be passed in a single, 
-     * self contained, JWT. 
-     * 
+     * Set condition for whether the authentication request parameters should be passed in a single,
+     * self contained, JWT.
+     *
      * @param condition condition to set
      */
     public void setUseRequestObjectPredicate(
@@ -191,9 +299,9 @@ public class OIDCAuthorizationConfiguration extends AbstractOIDCSSOConfiguration
 
     /**
      * Get whether to encode consent in authorization code and access/refresh tokens.
-     * 
+     *
      * @param profileRequestContext profile request context
-     * 
+     *
      * @return whether to encode consent in authorization code and access/refresh tokens
      */
     public boolean isEncodeConsentInTokens(@Nullable final ProfileRequestContext profileRequestContext) {
@@ -202,7 +310,7 @@ public class OIDCAuthorizationConfiguration extends AbstractOIDCSSOConfiguration
 
     /**
      * Set whether to encode consent in authorization code and access/refresh tokens.
-     * 
+     *
      * @param flag flag to set
      */
     public void setEncodeConsentInTokens(final boolean flag) {
@@ -211,26 +319,55 @@ public class OIDCAuthorizationConfiguration extends AbstractOIDCSSOConfiguration
 
     /**
      * Set condition for whether to encode consent in authorization code and access/refresh tokens.
-     * 
+     *
      * @param condition condition to set
      */
     public void setEncodeConsentInTokensPredicate(@Nonnull final Predicate<ProfileRequestContext> condition) {
         encodeConsentInTokensPredicate = Constraint.isNotNull(condition, "Condition cannot be null");
     }
-    
+
+    /**
+     * Get whether to make a request to the UserInfo Endpoint to obtain authenticated End-User claims.
+     *
+     * @param profileRequestContext profile request context
+     *
+     * @return whether to make a request to the UserInfo Endpoint
+     */
+    public boolean isRetrieveUserInfoEndpointClaims(@Nonnull final ProfileRequestContext profileRequestContext) {
+        return retrieveUserInfoEndpointClaims.test(profileRequestContext);
+    }
+
+    /**
+     * Set whether to make a request to the UserInfo Endpoint to obtain authenticated End-User claims.
+     *
+     * @param flag flag to set
+     */
+    public void setRetrieveUserInfoEndpointClaims(final boolean flag) {
+        retrieveUserInfoEndpointClaims = flag ? Predicates.alwaysTrue() : Predicates.alwaysFalse();
+    }
+
+    /**
+     * Set condition for whether to make a request to the UserInfo Endpoint to obtain authenticated End-User claims.
+     *
+     * @param condition condition to set
+     */
+    public void setRetrieveUserInfoEndpointClaims(@Nonnull final Predicate<ProfileRequestContext> condition){
+        retrieveUserInfoEndpointClaims = Constraint.isNotNull(condition, "Condition cannot be null");
+    }
+
     /**
      * Get authz code lifetime.
-     * 
+     *
      * <p>Defaults to 5 minutes.</p>
-     * 
+     *
      * @param profileRequestContext profile request context
-     * 
+     *
      * @return authz code lifetime
      */
     @Positive @Nonnull
     public Duration getAuthorizeCodeLifetime(@Nullable final ProfileRequestContext profileRequestContext) {
         final Duration lifetime = authorizeCodeLifetimeLookupStrategy.apply(profileRequestContext);
-        
+
         Constraint.isTrue(lifetime != null && !lifetime.isZero() && !lifetime.isNegative(),
                 "Authorization code lifetime must be greater than 0");
         return lifetime;
@@ -238,16 +375,16 @@ public class OIDCAuthorizationConfiguration extends AbstractOIDCSSOConfiguration
 
     /**
      * Set the lifetime of authz code.
-     * 
+     *
      * @param lifetime lifetime of authz code
      */
     public void setAuthorizeCodeLifetime(@Positive @Nonnull final Duration lifetime) {
         Constraint.isTrue(lifetime != null && !lifetime.isZero() && !lifetime.isNegative(),
                 "Authorization code lifetime must be greater than 0");
-        
+
         authorizeCodeLifetimeLookupStrategy = FunctionSupport.constant(lifetime);
     }
-    
+
     /**
      * Set a lookup strategy for the authz code lifetime.
      *
@@ -257,32 +394,32 @@ public class OIDCAuthorizationConfiguration extends AbstractOIDCSSOConfiguration
             @Nonnull final Function<ProfileRequestContext,Duration> strategy) {
         authorizeCodeLifetimeLookupStrategy = Constraint.isNotNull(strategy, "Lookup strategy cannot be null");
     }
-    
+
     /**
      * Set a lookup strategy to determine the HTTP request method for an authentication request.
-     * 
+     *
      * @param strategy the strategy to set.
      */
     public void setHttpRequestMethodLookupStrategy(
             @Nonnull final Function<ProfileRequestContext, String> strategy) {
-        httpRequestMethodLookupStrategy = 
+        httpRequestMethodLookupStrategy =
                 Constraint.isNotNull(strategy, "HTTP request method strategy can not be null");
     }
-    
+
     /**
      * Set the HTTP request method for an authentication request.
-     * 
+     *
      * @param method the HTTP method to set, either POST or GET.
      */
     public void setHttpRequestMethod(@Nonnull @NotEmpty final OIDCHttpRequestMethod method){
         httpRequestMethodLookupStrategy = FunctionSupport.constant(method != null ? method.toString() : null);
     }
-    
+
     /**
      * Get the HTTP request method for an authentication request.
-     * 
+     *
      * @param profileRequestContext profile request context
-     * 
+     *
      * @return the HTTP request method
      */
     public OIDCHttpRequestMethod getHttpRequestMethod(@Nullable final ProfileRequestContext profileRequestContext) {
@@ -301,14 +438,14 @@ public class OIDCAuthorizationConfiguration extends AbstractOIDCSSOConfiguration
     /**
      * Get the set of attribute IDs which should be encoded in encrypted form into the authorization code
      * and/or access/refresh tokens to enable recovery on the back-channel.
-     * 
+     *
      * @param profileRequestContext profile request context
-     * 
+     *
      * @return the attribute IDs to encode
      */
     @Nonnull @NonnullElements @NotLive public Set<String> getEncodedAttributes(
             @Nullable final ProfileRequestContext profileRequestContext) {
-        
+
         final Set<String> attributes = encodedAttributesLookupStrategy.apply(profileRequestContext);
         if (attributes != null) {
             return Set.copyOf(attributes);
@@ -319,7 +456,7 @@ public class OIDCAuthorizationConfiguration extends AbstractOIDCSSOConfiguration
     /**
      * Set the set of attribute IDs which should be encoded in encrypted form into the authorization code
      * and/or access/refresh tokens to enable recovery on the back-channel.
-     * 
+     *
      * @param attributes the attribute IDs to encode
      */
     public void setEncodedAttributes(@Nullable @NonnullElements final Collection<String> attributes) {
@@ -345,17 +482,17 @@ public class OIDCAuthorizationConfiguration extends AbstractOIDCSSOConfiguration
 
     /**
      * Get the set of attribute IDs which should be omitted from the UserInfo token.
-     * 
+     *
      * <p>Default behavior is to include all claims, but omiited claims also affect the set that
      * may need to be embedded for recovery into the access/refresh tokens.</p>
-     * 
+     *
      * @param profileRequestContext profile request context
-     * 
+     *
      * @return the attribute IDs to omit from UserInfo token
      */
     @Nonnull @NonnullElements @NotLive public Set<String> getDeniedUserInfoAttributes(
             @Nullable final ProfileRequestContext profileRequestContext) {
-        
+
         final Set<String> attributes = deniedUserInfoAttributesLookupStrategy.apply(profileRequestContext);
         if (attributes != null) {
             return Set.copyOf(attributes);
@@ -365,10 +502,10 @@ public class OIDCAuthorizationConfiguration extends AbstractOIDCSSOConfiguration
 
     /**
      * Set the set of attribute IDs which should be omitted from the UserInfo token.
-     * 
+     *
      * <p>Default behavior is to include all claims, but omiited claims also affect the set that
      * may need to be embedded for recovery into the access/refresh tokens.</p>
-     * 
+     *
      * @param attributes the attribute IDs to omit from UserInfo token
      */
     public void setDeniedUserInfoAttributes(@Nullable @NonnullElements final Collection<String> attributes) {
@@ -393,11 +530,11 @@ public class OIDCAuthorizationConfiguration extends AbstractOIDCSSOConfiguration
 
     /**
      * Get whether to include iss parameter in the authentication response.
-     * 
+     *
      * @param profileRequestContext profile request context
-     * 
+     *
      * @return whether to include iss parameter in the authentication response
-     * 
+     *
      * @since 2.1.0
      */
     public boolean isIncludeIssuerInResponse(@Nullable final ProfileRequestContext profileRequestContext) {
@@ -406,9 +543,9 @@ public class OIDCAuthorizationConfiguration extends AbstractOIDCSSOConfiguration
 
     /**
      * Set whether to include iss parameter in the authentication response.
-     * 
+     *
      * @param flag flag to set
-     * 
+     *
      * @since 2.1.0
      */
     public void setIncludeIssuerInResponse(final boolean flag) {
@@ -417,9 +554,9 @@ public class OIDCAuthorizationConfiguration extends AbstractOIDCSSOConfiguration
 
     /**
      * Set condition for whether to include iss parameter in the authentication response.
-     * 
+     *
      * @param condition condition to set
-     * 
+     *
      * @since 2.1.0
      */
     public void setIncludeIssuerInResponsePredicate(@Nonnull final Predicate<ProfileRequestContext> condition) {
@@ -428,11 +565,11 @@ public class OIDCAuthorizationConfiguration extends AbstractOIDCSSOConfiguration
 
     /**
      * Get the bi-function for manipulating authorization code claims set.
-     * 
+     *
      * @param profileRequestContext profile request context
-     * 
+     *
      * @return the bi-function for manipulating authorization code claims set
-     * 
+     *
      * @since 2.1.0
      */
     @Nonnull
@@ -444,9 +581,9 @@ public class OIDCAuthorizationConfiguration extends AbstractOIDCSSOConfiguration
 
     /**
      * Set the bi-function for manipulating authorization code claims set.
-     * 
+     *
      * @param strategy bi-function for manipulating authorization code claims set
-     * 
+     *
      * @since 2.1.0
      */
     public void setAuthorizationCodeClaimsSetManipulationStrategy(
@@ -458,14 +595,14 @@ public class OIDCAuthorizationConfiguration extends AbstractOIDCSSOConfiguration
      * Set a lookup strategy for the bi-function for manipulating authorization code claims set.
      *
      * @param strategy lookup strategy
-     * 
+     *
      * @since 2.1.0
      */
-    public void setAuthorizationCodeClaimsSetManipulationStrategyLookupStrategy(@Nonnull final 
+    public void setAuthorizationCodeClaimsSetManipulationStrategyLookupStrategy(@Nonnull final
             Function<ProfileRequestContext,BiFunction<ProfileRequestContext,Map<String,Object>,Map<String,Object>>>
             strategy) {
         authorizationCodeClaimsSetManipulationStrategyLookupStrategy = Constraint.isNotNull(strategy,
                 "Lookup strategy cannot be null");
     }
 
-}
\ No newline at end of file
+}
diff --git a/oidc-common-profile-api/src/main/java/net/shibboleth/oidc/profile/config/OIDCSecurityConfiguration.java b/oidc-common-profile-api/src/main/java/net/shibboleth/oidc/profile/config/OIDCSecurityConfiguration.java
index 950df20..f948cb4 100644
--- a/oidc-common-profile-api/src/main/java/net/shibboleth/oidc/profile/config/OIDCSecurityConfiguration.java
+++ b/oidc-common-profile-api/src/main/java/net/shibboleth/oidc/profile/config/OIDCSecurityConfiguration.java
@@ -18,6 +18,7 @@
 package net.shibboleth.oidc.profile.config;
 
 import javax.annotation.Nullable;
+
 import org.opensaml.xmlsec.EncryptionConfiguration;
 import org.opensaml.xmlsec.SignatureSigningConfiguration;
 
@@ -40,6 +41,10 @@ public class OIDCSecurityConfiguration extends SecurityConfiguration {
     /** Configuration used when validating token endpoint authentication JWT signatures. */
     @Nullable
     private SignatureSigningConfiguration tokenEndpointJwtSignatureValidationConfig;
+    
+    /** Configuration used when validating id_token JWT signatures. */
+    @Nullable
+    private net.shibboleth.oidc.security.SignatureValidationConfiguration<?> idTokenJwtSignatureValidationConfig;
 
     /**
      * Get the configuration used when decrypting request object information.
@@ -98,5 +103,24 @@ public class OIDCSecurityConfiguration extends SecurityConfiguration {
             @Nullable final SignatureSigningConfiguration config) {
         tokenEndpointJwtSignatureValidationConfig = config;
     }
+    
+    /**
+     * Get the configuration used when validating id_token JWT signatures.
+     * 
+     * @return configuration used when validating id_token JWT signatures, or null
+     */
+    @Nullable
+    public net.shibboleth.oidc.security.SignatureValidationConfiguration<?> getIdTokenJwtSignatureValidationConfig() {
+        return idTokenJwtSignatureValidationConfig;
+    }
+
+    /**
+     * Set the configuration used when validating id_token JWT signatures.
+     * 
+     * @param config configuration used when validating id_token JWT signatures, or null
+     */
+    public void setIdTokenJwtSignatureValidationConfig(@Nullable final  net.shibboleth.oidc.security.SignatureValidationConfiguration<?> config) {
+        idTokenJwtSignatureValidationConfig = config;
+    }
 
 }
diff --git a/pom.xml b/pom.xml
index af42654..337553a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -18,7 +18,7 @@
         <idp.groupId>net.shibboleth.idp</idp.groupId>
         <idp.version>4.2.0</idp.version>
         <opensaml.groupId>org.opensaml</opensaml.groupId>
-        <opensaml.version>4.2.0</opensaml.version>
+        <opensaml.version>4.2.1-SNAPSHOT</opensaml.version>
         <java-support.version>8.3.1</java-support.version>
         <nimbus-jose-jwt.version>9.14</nimbus-jose-jwt.version>
         <oauth2-oidc-sdk.version>9.20</oauth2-oidc-sdk.version>

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list