[java-oidc-common] branch main updated: JCOMOIDC-22 - Move ProviderMetadataResolver implementations from OP to commons

Phil Smart philip.smart at jisc.ac.uk
Fri Aug 6 13:06:25 UTC 2021


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

philsmart pushed a commit to branch main
in repository java-oidc-common.

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

The following commit(s) were added to refs/heads/main by this push:
       new  12a57e8   JCOMOIDC-22 - Move ProviderMetadataResolver implementations from OP to commons
12a57e8 is described below

commit 12a57e8761033cce5a7eda38030bc15480450161
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Fri Aug 6 14:06:19 2021 +0100

    JCOMOIDC-22 - Move ProviderMetadataResolver implementations from OP to
    commons
    
    - Moved ProviderMetadataResolver related classes into oidc-common.
    Changed the resolver public API from accepting a PRC criteria type to a
    CriteriaSet.
     - Added new chaining providers
     - Converted the serviceable provider to a container type.
    
    https://issues.shibboleth.net/jira/browse/JCOMOIDC-22
---
 oidc-common-metadata-api/pom.xml                   |   7 +-
 ...java => ProviderMetadataProviderContainer.java} | 102 +++--------
 .../oidc/metadata/ProviderMetadataResolver.java    |  37 ++++
 .../RefreshableProviderMetadataResolver.java       |  60 ++++++
 .../RelyingPartyClientInformationProvider.java     |   2 +-
 .../oidc/metadata/criterion/IssuerIDCriterion.java |  73 ++++++++
 oidc-common-metadata-impl/pom.xml                  |   5 +
 .../metadata/impl/AbstractOIDCEntityResolver.java  |  15 ++
 .../impl/ChainingProviderMetadataResolver.java     | 202 +++++++++++++++++++++
 .../impl/FilesystemProviderMetadataResolver.java   | 146 +++++++++++++++
 .../impl/ReloadingProviderMetadataProvider.java    |  90 +++++++++
 .../ProviderMetadataResolverServiceStrategy.java   | 101 +++++++++++
 .../impl/ChainingProviderMetadataResolverTest.java |  59 ++++++
 ...loadClientResolverServiceConfigurationTest.java |   1 -
 ...rMetadataResolverServiceConfigurationTest.java} |  37 ++--
 .../impl/oidc-provider-metadata-providers.xml      |  40 ++++
 .../impl/oidc-provider-metadata-providers2.xml     |  56 ++++++
 .../oidc/metadata/impl/openid-configuration.json   |  55 ++++++
 18 files changed, 983 insertions(+), 105 deletions(-)

diff --git a/oidc-common-metadata-api/pom.xml b/oidc-common-metadata-api/pom.xml
index 10b0186..88e8daf 100644
--- a/oidc-common-metadata-api/pom.xml
+++ b/oidc-common-metadata-api/pom.xml
@@ -23,7 +23,12 @@
             <groupId>net.shibboleth.idp</groupId>
             <artifactId>idp-core</artifactId>
             <scope>provided</scope>
-        </dependency>       
+        </dependency>          
+         <dependency>
+            <groupId>${opensaml.groupId}</groupId>
+            <artifactId>opensaml-profile-api</artifactId>
+            <scope>provided</scope>
+        </dependency>      
         <dependency>
             <groupId>com.nimbusds</groupId>
             <artifactId>oauth2-oidc-sdk</artifactId>
diff --git a/oidc-common-metadata-api/src/main/java/net/shibboleth/oidc/metadata/RelyingPartyClientInformationProvider.java b/oidc-common-metadata-api/src/main/java/net/shibboleth/oidc/metadata/ProviderMetadataProviderContainer.java
similarity index 50%
copy from oidc-common-metadata-api/src/main/java/net/shibboleth/oidc/metadata/RelyingPartyClientInformationProvider.java
copy to oidc-common-metadata-api/src/main/java/net/shibboleth/oidc/metadata/ProviderMetadataProviderContainer.java
index 228f898..03a6253 100644
--- a/oidc-common-metadata-api/src/main/java/net/shibboleth/oidc/metadata/RelyingPartyClientInformationProvider.java
+++ b/oidc-common-metadata-api/src/main/java/net/shibboleth/oidc/metadata/ProviderMetadataProviderContainer.java
@@ -17,51 +17,40 @@
 
 package net.shibboleth.oidc.metadata;
 
-import java.time.Instant;
-
 import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.common.base.Objects;
-import com.nimbusds.openid.connect.sdk.rp.OIDCClientInformation;
 
 import net.shibboleth.ext.spring.service.AbstractServiceableComponent;
 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;
 
 /**
- * This class exists primarily to allow the parsing of relying-party.xml to create a serviceable implementation of
- * {@link ClientInformationResolver}. Based on net.shibboleth.idp.saml.metadata.RelyingPartyMetadataProvider.
+ * This class is a sortable container of {@link ProviderMetadataResolver}s, wrapped into a serviceable component.
+ * 
  */
-
-public class RelyingPartyClientInformationProvider extends AbstractServiceableComponent<ClientInformationResolver>
-        implements RefreshableClientInformationResolver, Comparable<RelyingPartyClientInformationProvider> {
+public class ProviderMetadataProviderContainer extends AbstractServiceableComponent<ProviderMetadataResolver>
+                                               implements Comparable<ProviderMetadataProviderContainer> {
 
     /** If we autogenerate a sort key it comes from this count. */
     private static int sortKeyValue;
 
     /** Class logger. */
-    private final Logger log = LoggerFactory.getLogger(RelyingPartyClientInformationProvider.class);
+    private final Logger log = LoggerFactory.getLogger(ProviderMetadataProviderContainer.class);
 
     /** The embedded resolver. */
     @NonnullAfterInit
-    private ClientInformationResolver resolver;
+    private ProviderMetadataResolver resolver;
 
     /** The key by which we sort the provider. */
     @NonnullAfterInit
     private Integer sortKey;
 
-    /** Constructor. */
-    public RelyingPartyClientInformationProvider() {
-    }
-
     /**
      * Set the sort key.
      * 
@@ -69,18 +58,18 @@ public class RelyingPartyClientInformationProvider extends AbstractServiceableCo
      */
     public void setSortKey(final int key) {
         ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-        sortKey = Integer.valueOf(key);
+        sortKey = key;
     }
 
     /**
-     * Set the {@link ClientInformationResolver} to embed.
+     * Set the {@link ProviderMetadataResolver} to embed.
      * 
-     * @param theResolver The {@link ClientInformationResolver} to embed.
+     * @param theResolver The {@link ProviderMetadataResolver} to embed.
      */
     @Nonnull
-    public void setEmbeddedResolver(@Nonnull final ClientInformationResolver theResolver) {
+    public void setEmbeddedResolver(@Nonnull final ProviderMetadataResolver theResolver) {
         ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-        resolver = Constraint.isNotNull(theResolver, "ClientInformationResolver cannot be null");
+        resolver = Constraint.isNotNull(theResolver, "ProviderMetadataResolver cannot be null");
     }
 
     /**
@@ -89,83 +78,38 @@ public class RelyingPartyClientInformationProvider extends AbstractServiceableCo
      * @return the parameter we got as a constructor
      */
     @Nonnull
-    public ClientInformationResolver getEmbeddedResolver() {
+    public ProviderMetadataResolver getEmbeddedResolver() {
         return resolver;
     }
 
-    /** {@inheritDoc} */
-    @Override
-    @Nonnull
-    public Iterable<OIDCClientInformation> resolve(@Nullable final CriteriaSet criteria) throws ResolverException {
 
-        return resolver.resolve(criteria);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    @Nullable
-    public OIDCClientInformation resolveSingle(@Nullable final CriteriaSet criteria) throws ResolverException {
-
-        return resolver.resolveSingle(criteria);
-    }
-
-    /** {@inheritDoc} */
     @Override
     protected void doInitialize() throws ComponentInitializationException {
         setId(resolver.getId());
         super.doInitialize();
         if (null == resolver) {
-            throw new ComponentInitializationException("ClientInformationResolver cannot be null");
+            throw new ComponentInitializationException("ProviderMetadataResolver cannot be null");
         }
 
         if (null == sortKey) {
-            synchronized (this) {
+            synchronized (ProviderMetadataProviderContainer.class) {
                 sortKeyValue++;
-                sortKey = Integer.valueOf(sortKeyValue);
+                setSortKey(sortKeyValue);
             }
-            log.info("Top level ClientInformation Provider '{}' did not have a sort key; giving it value '{}'", getId(),
+            log.info("Top level ProviderMetadata Provider '{}' did not have a sort key; giving it value '{}'", getId(),
                     sortKey);
         }
     }
+    
 
-    /** {@inheritDoc} */
     @Override
     @Nonnull
-    public ClientInformationResolver getComponent() {
-        return this;
+    public ProviderMetadataResolver getComponent() {
+        return getEmbeddedResolver();
     }
 
-    /** {@inheritDoc} */
     @Override
-    public void refresh() throws ResolverException {
-        if (resolver instanceof RefreshableClientInformationResolver) {
-            ((RefreshableClientInformationResolver) resolver).refresh();
-        }
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public Instant getLastRefresh() {
-        if (resolver instanceof RefreshableClientInformationResolver) {
-            return ((RefreshableClientInformationResolver) resolver).getLastRefresh();
-        } else {
-            return null;
-        }
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public Instant getLastUpdate() {
-        if (resolver instanceof RefreshableClientInformationResolver) {
-            return ((RefreshableClientInformationResolver) resolver).getLastUpdate();
-        } else {
-            return null;
-        }
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public int compareTo(final RelyingPartyClientInformationProvider other) {
+    public int compareTo(final ProviderMetadataProviderContainer other) {
         ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
         final int result = sortKey.compareTo(other.sortKey);
         if (result != 0) {
@@ -186,18 +130,16 @@ public class RelyingPartyClientInformationProvider extends AbstractServiceableCo
         if (null == other) {
             return false;
         }
-        if (!(other instanceof RelyingPartyClientInformationProvider)) {
+        if (!(other instanceof ProviderMetadataProviderContainer)) {
             return false;
         }
-        final RelyingPartyClientInformationProvider otherRp = (RelyingPartyClientInformationProvider) other;
+        final ProviderMetadataProviderContainer otherRp = (ProviderMetadataProviderContainer) other;
 
         return Objects.equal(otherRp.sortKey, sortKey) && Objects.equal(getId(), otherRp.getId());
     }
 
-    /** {@inheritDoc} */
     @Override
     public int hashCode() {
         return Objects.hashCode(sortKey, getId());
     }
-
 }
\ No newline at end of file
diff --git a/oidc-common-metadata-api/src/main/java/net/shibboleth/oidc/metadata/ProviderMetadataResolver.java b/oidc-common-metadata-api/src/main/java/net/shibboleth/oidc/metadata/ProviderMetadataResolver.java
new file mode 100644
index 0000000..0beb81a
--- /dev/null
+++ b/oidc-common-metadata-api/src/main/java/net/shibboleth/oidc/metadata/ProviderMetadataResolver.java
@@ -0,0 +1,37 @@
+/*
+ * 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.metadata;
+
+import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
+
+import net.shibboleth.oidc.metadata.criterion.IssuerIDCriterion;
+import net.shibboleth.utilities.java.support.component.IdentifiedComponent;
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+import net.shibboleth.utilities.java.support.resolver.Resolver;
+
+/**
+ * A resolver that is capable of resolving {@link OIDCProviderMetadata} instances which meet certain supplied criteria.
+ * <p>
+ * At a minimum, a {@link OIDCProviderMetadata} implementation MUST support the following criteria:</p>
+ * <ul>
+ * <li>{@link IssuerIDCriterion}</li>
+ * </ul>
+ */
+public interface ProviderMetadataResolver extends Resolver<OIDCProviderMetadata, CriteriaSet>, IdentifiedComponent {
+
+}
diff --git a/oidc-common-metadata-api/src/main/java/net/shibboleth/oidc/metadata/RefreshableProviderMetadataResolver.java b/oidc-common-metadata-api/src/main/java/net/shibboleth/oidc/metadata/RefreshableProviderMetadataResolver.java
new file mode 100644
index 0000000..f7e4d24
--- /dev/null
+++ b/oidc-common-metadata-api/src/main/java/net/shibboleth/oidc/metadata/RefreshableProviderMetadataResolver.java
@@ -0,0 +1,60 @@
+/*
+ * 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.metadata;
+
+import java.time.Instant;
+
+import javax.annotation.Nullable;
+
+import net.shibboleth.utilities.java.support.resolver.ResolverException;
+
+/**
+ * Specialization of {@link ProviderMetadataResolver} that supports on-demand refresh.
+ */
+public interface RefreshableProviderMetadataResolver extends ProviderMetadataResolver {
+
+    /**
+     * Refresh the data exposed by the resolver.
+     * 
+     * <p>
+     * An implementation of this method should typically be either <code>synchronized</code>
+     * or make use other locking mechanisms to protect against concurrent access.
+     * </p>
+     * 
+     * @throws ResolverException if the refresh operation was unsuccessful
+     */
+    void refresh() throws ResolverException;
+
+    /**
+     * Gets the time the last refresh cycle occurred.
+     * 
+     * @return time the last refresh cycle occurred
+     */
+    @Nullable Instant getLastRefresh();
+
+    /**
+     * Get the time that the currently available metadata was last updated. Note, this may be different than
+     * the time retrieved by {@link #getLastRefresh()} is the metadata was known not to have changed during
+     * the last refresh cycle.
+     * 
+     * @return time when the currently metadata was last updated, null if it has never successfully been read
+     * in
+     */
+    @Nullable Instant getLastUpdate();
+
+}
\ No newline at end of file
diff --git a/oidc-common-metadata-api/src/main/java/net/shibboleth/oidc/metadata/RelyingPartyClientInformationProvider.java b/oidc-common-metadata-api/src/main/java/net/shibboleth/oidc/metadata/RelyingPartyClientInformationProvider.java
index 228f898..281170a 100644
--- a/oidc-common-metadata-api/src/main/java/net/shibboleth/oidc/metadata/RelyingPartyClientInformationProvider.java
+++ b/oidc-common-metadata-api/src/main/java/net/shibboleth/oidc/metadata/RelyingPartyClientInformationProvider.java
@@ -40,7 +40,7 @@ import net.shibboleth.utilities.java.support.resolver.ResolverException;
  * This class exists primarily to allow the parsing of relying-party.xml to create a serviceable implementation of
  * {@link ClientInformationResolver}. Based on net.shibboleth.idp.saml.metadata.RelyingPartyMetadataProvider.
  */
-
+//TODO should change to a Container e.g. ProviderMetadataProviderContainer.java
 public class RelyingPartyClientInformationProvider extends AbstractServiceableComponent<ClientInformationResolver>
         implements RefreshableClientInformationResolver, Comparable<RelyingPartyClientInformationProvider> {
 
diff --git a/oidc-common-metadata-api/src/main/java/net/shibboleth/oidc/metadata/criterion/IssuerIDCriterion.java b/oidc-common-metadata-api/src/main/java/net/shibboleth/oidc/metadata/criterion/IssuerIDCriterion.java
new file mode 100644
index 0000000..67ace84
--- /dev/null
+++ b/oidc-common-metadata-api/src/main/java/net/shibboleth/oidc/metadata/criterion/IssuerIDCriterion.java
@@ -0,0 +1,73 @@
+package net.shibboleth.oidc.metadata.criterion;
+
+import java.util.Objects;
+
+import javax.annotation.Nonnull;
+import javax.annotation.concurrent.Immutable;
+
+import com.nimbusds.oauth2.sdk.id.Issuer;
+
+import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.utilities.java.support.resolver.Criterion;
+
+/**
+ * A {@link Criterion} representing an OIDC issuer ID.
+ */
+ at Immutable
+public final class IssuerIDCriterion implements Criterion {
+    
+    /** The issuer ID. */
+    @Nonnull @NotEmpty private final Issuer id;
+    
+    /**
+     * 
+     * Constructor.
+     *
+     * @param issuerId the id of the issuer, can not be null or empty
+     */
+    public IssuerIDCriterion(@Nonnull final Issuer issuerId) {
+        id = Constraint.isNotNull(issuerId, "Issuer ID can not be null");
+    }
+    
+    /**
+     * Get the issuer ID.
+     * 
+     * @return the issuer ID. 
+     */
+    @Nonnull public Issuer getIssuerID() {
+        return id;
+    }
+    
+    
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return "IssuerIDCriterion [id=" + id + "]";
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return Objects.hash(id);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        IssuerIDCriterion other = (IssuerIDCriterion) obj;
+        return id.equals(other.id);
+    }
+
+    
+    
+    
+
+}
diff --git a/oidc-common-metadata-impl/pom.xml b/oidc-common-metadata-impl/pom.xml
index 5d83809..43bb419 100644
--- a/oidc-common-metadata-impl/pom.xml
+++ b/oidc-common-metadata-impl/pom.xml
@@ -31,6 +31,11 @@
 			<artifactId>spring-extensions</artifactId>
 			<scope>provided</scope>
 		</dependency>
+        <dependency>
+            <groupId>net.shibboleth.idp</groupId>
+            <artifactId>idp-profile-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
 		<dependency>
 			<groupId>org.opensaml</groupId>
 			<artifactId>opensaml-profile-api</artifactId>
diff --git a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/impl/AbstractOIDCEntityResolver.java b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/impl/AbstractOIDCEntityResolver.java
index 0507a7b..a3ef4af 100644
--- a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/impl/AbstractOIDCEntityResolver.java
+++ b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/impl/AbstractOIDCEntityResolver.java
@@ -55,6 +55,9 @@ public abstract class AbstractOIDCEntityResolver<Key extends Identifier, Value>
     /** Backing store for runtime JSON data. */
     private JsonBackingStore jsonBackingStore;
     
+    /** Logging prefix. */
+    private String logPrefix;
+    
     /**
      * Whether problems during initialization should cause the provider to fail or go on without metadata. The
      * assumption being that in most cases a provider will recover at some point in the future. Default: true.
@@ -67,6 +70,18 @@ public abstract class AbstractOIDCEntityResolver<Key extends Identifier, Value>
     public AbstractOIDCEntityResolver() {
         failFastInitialization = true;
     }
+    
+    /**
+     * Return a prefix for logging messages for this component.
+     * 
+     * @return a string for insertion at the beginning of any log messages
+     */
+    @Nonnull @NotEmpty protected String getLogPrefix() {
+        if (logPrefix == null) {
+            logPrefix = String.format("Metadata Resolver %s %s:", getClass().getSimpleName(), getId());
+        }
+        return logPrefix;
+    }
 
     /**
      * Gets whether problems during initialization should cause the provider to fail or go on without metadata. The
diff --git a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/impl/ChainingProviderMetadataResolver.java b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/impl/ChainingProviderMetadataResolver.java
new file mode 100644
index 0000000..c79a56d
--- /dev/null
+++ b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/impl/ChainingProviderMetadataResolver.java
@@ -0,0 +1,202 @@
+package net.shibboleth.oidc.metadata.impl;
+
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
+
+import net.shibboleth.oidc.metadata.ProviderMetadataResolver;
+import net.shibboleth.oidc.metadata.RefreshableProviderMetadataResolver;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
+import net.shibboleth.utilities.java.support.annotation.constraint.NotLive;
+import net.shibboleth.utilities.java.support.annotation.constraint.Unmodifiable;
+import net.shibboleth.utilities.java.support.component.AbstractIdentifiableInitializableComponent;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+import net.shibboleth.utilities.java.support.resolver.ResolverException;
+
+/**
+ * A provider metadata provider that uses registered resolvers, in turn, to answer queries.
+ * 
+ * The Iterable of provider metadata returned is the first non-null and non-empty Iterable found while iterating over
+ * the registered resolvers in resolver list order.
+ */
+public class ChainingProviderMetadataResolver extends AbstractIdentifiableInitializableComponent implements 
+    RefreshableProviderMetadataResolver {
+    
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(ChainingProviderMetadataResolver.class);
+    
+    /** Registered resolvers. */
+    @Nonnull @NonnullElements private List<ProviderMetadataResolver> resolvers;
+    
+    /** Constructor. */
+    public ChainingProviderMetadataResolver() {
+        resolvers = Collections.emptyList();
+    }
+    
+    /**
+     * Get an immutable the list of currently registered resolvers.
+     * 
+     * @return list of currently registered resolvers
+     */
+    @Nonnull @NonnullElements @Unmodifiable @NotLive public List<ProviderMetadataResolver> getResolvers() {
+        return resolvers;
+    }
+    
+    /**
+     * Set the registered provider metadata resolvers.
+     * 
+     * @param newResolvers the provider metadata resolvers to use
+     * 
+     * @throws ResolverException thrown if there is a problem adding the provider metadata resolvers
+     */
+    public void setResolvers(@Nullable @NonnullElements final List<? extends ProviderMetadataResolver> newResolvers)
+            throws ResolverException {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+
+        if (newResolvers == null || newResolvers.isEmpty()) {
+            resolvers = Collections.emptyList();
+        } else {
+            resolvers = List.copyOf(newResolvers);
+        }
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    @Nullable public OIDCProviderMetadata resolveSingle(@Nullable final CriteriaSet criteria) 
+            throws ResolverException {
+        ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
+
+        final Iterable<OIDCProviderMetadata> iterable = resolve(criteria);
+        if (iterable != null) {
+            final Iterator<OIDCProviderMetadata> iterator = iterable.iterator();
+            if (iterator != null && iterator.hasNext()) {
+                return iterator.next();
+            }
+        }
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    @Nonnull public Iterable<OIDCProviderMetadata> resolve(@Nullable final CriteriaSet criteria) 
+            throws ResolverException {
+        ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
+
+        for (final ProviderMetadataResolver resolver : resolvers) {
+            try {
+                final Iterable<OIDCProviderMetadata> providerMetadatas = resolver.resolve(criteria);
+                if (providerMetadatas != null && providerMetadatas.iterator().hasNext()) {
+                    return providerMetadatas;
+                }
+            } catch (final ResolverException e) {
+                log.warn("Error retrieving provider metadata from resolver of type {}, proceeding to next resolver",
+                        resolver.getClass().getName(), e);
+                continue;
+            }
+        }
+
+        return Collections.emptyList();
+    }
+
+    /** {@inheritDoc} */
+    @Override public void refresh() throws ResolverException {
+        for (final ProviderMetadataResolver resolver : resolvers) {
+            if (resolver instanceof RefreshableProviderMetadataResolver) {
+                ((RefreshableProviderMetadataResolver) resolver).refresh();
+            }
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    @Nullable public Instant getLastUpdate() {
+        Instant ret = null;
+        for (final ProviderMetadataResolver resolver : resolvers) {
+            if (resolver instanceof RefreshableProviderMetadataResolver) {
+                final Instant lastUpdate = ((RefreshableProviderMetadataResolver) resolver).getLastUpdate();
+                if (ret == null || ret.isBefore(lastUpdate)) {
+                    ret = lastUpdate;
+                }
+            }
+        }
+        
+        return ret;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    @Nullable public Instant getLastRefresh() {
+        Instant ret = null;
+        for (final ProviderMetadataResolver resolver : resolvers) {
+            if (resolver instanceof RefreshableProviderMetadataResolver) {
+                final Instant lastRefresh = ((RefreshableProviderMetadataResolver) resolver).getLastRefresh();
+                if (ret == null || ret.isBefore(lastRefresh)) {
+                    ret = lastRefresh;
+                }
+            }
+        }
+        
+        return ret;
+    }
+    
+    /** {@inheritDoc} */
+    @Override protected void doInitialize() throws ComponentInitializationException {
+        super.doInitialize();
+        if (resolvers == null) {
+            log.warn("ProviderMetadataResolver was not configured with any member " + 
+                    "ProviderMetadataResolvers");
+            resolvers = Collections.emptyList();
+        } else {
+            final List<String> resolverDetails = new ArrayList<>();
+            for (final ProviderMetadataResolver resolver : resolvers) {
+                resolverDetails.add(resolver.getId() + ": " + countClients(resolver) + " clients");
+            }
+            log.info("ChainingProviderMetadataResolver was configured with the following resolvers: {}",
+                    resolverDetails);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void doDestroy() {
+        super.doDestroy();
+        resolvers = Collections.emptyList();
+    }
+    
+    /**
+     * Counts the clients found from the given resolver.
+     * 
+     * @param resolver The resolver whose clients are counted.
+     * @return The amount of resolvable clients.
+     */
+    protected int countClients(final ProviderMetadataResolver resolver) {
+        int count = 0;
+        final Iterable<OIDCProviderMetadata> iterable;
+        try {
+            iterable = resolver.resolve(new CriteriaSet());
+        } catch (final ResolverException e) {
+            log.warn("ChainingProviderMetadataResolver could not count clients for {}", resolver.getId());
+            return 0;
+        }
+      
+        final Iterator<OIDCProviderMetadata> iterator = iterable.iterator();
+        while (iterator.hasNext()) {
+            iterator.next();
+            count++;
+        }       
+        return count;
+    }
+
+}
diff --git a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/impl/FilesystemProviderMetadataResolver.java b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/impl/FilesystemProviderMetadataResolver.java
new file mode 100644
index 0000000..32321df
--- /dev/null
+++ b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/impl/FilesystemProviderMetadataResolver.java
@@ -0,0 +1,146 @@
+/*
+ * 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.metadata.impl;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Timer;
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.core.io.Resource;
+
+import com.nimbusds.oauth2.sdk.ParseException;
+import com.nimbusds.oauth2.sdk.id.Issuer;
+import com.nimbusds.oauth2.sdk.util.JSONObjectUtils;
+import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
+
+import net.shibboleth.idp.profile.context.RelyingPartyContext;
+import net.shibboleth.oidc.metadata.RefreshableProviderMetadataResolver;
+import net.shibboleth.oidc.metadata.criterion.IssuerIDCriterion;
+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;
+
+/**
+ * Based on {@link org.opensaml.saml.metadata.resolver.impl.FilesystemMetadataResolver}.
+ */
+public class FilesystemProviderMetadataResolver extends AbstractFileOIDCEntityResolver<Issuer, OIDCProviderMetadata>
+        implements RefreshableProviderMetadataResolver {
+
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(FilesystemProviderMetadataResolver.class);
+
+    /**
+     * Strategy used to locate the {@link RelyingPartyContext} associated with a given {@link ProfileRequestContext}.
+     */
+    @Nonnull private Function<ProfileRequestContext, RelyingPartyContext> relyingPartyContextLookupStrategy;
+
+    /**
+     * Constructor.
+     * 
+     * @param metadata the metadata file
+     * 
+     * @throws IOException If the metedata cannot be loaded.
+     */
+    public FilesystemProviderMetadataResolver(@Nonnull final Resource metadata) throws IOException {
+        super(metadata);
+        relyingPartyContextLookupStrategy = new ChildContextLookup<>(RelyingPartyContext.class);
+    }
+
+    /**
+     * Constructor.
+     * 
+     * @param metadata the metadata file
+     * @param backgroundTaskTimer timer used to refresh metadata in the background
+     * 
+     * @throws IOException If the metedata cannot be loaded.
+     */
+    public FilesystemProviderMetadataResolver(@Nullable final Timer backgroundTaskTimer,
+            @Nonnull final Resource metadata) throws IOException {
+        super(backgroundTaskTimer, metadata);
+        relyingPartyContextLookupStrategy = new ChildContextLookup<>(RelyingPartyContext.class);
+    }
+
+    /**
+     * Set the strategy used to locate the {@link RelyingPartyContext} associated with a given
+     * {@link ProfileRequestContext}.
+     * 
+     * @param strategy strategy used to locate the {@link RelyingPartyContext} associated with a given
+     *            {@link ProfileRequestContext}
+     */
+    public void setRelyingPartyContextLookupStrategy(
+            @Nonnull final Function<ProfileRequestContext, RelyingPartyContext> strategy) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+        relyingPartyContextLookupStrategy =
+                Constraint.isNotNull(strategy, "RelyingPartyContext lookup strategy cannot be null");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    @Nullable public Iterable<OIDCProviderMetadata> resolve(@Nullable final CriteriaSet criteria)
+            throws ResolverException {
+        ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
+        ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+
+        final IssuerIDCriterion issuerIdCriterion = criteria.get(IssuerIDCriterion.class);
+        if (issuerIdCriterion == null) {
+            log.trace("No issuer ID criteria found, returning all");
+            return new ArrayList<>(getBackingStore().getOrderedInformation());
+        }
+        // TODO: support other criterion
+        return new ArrayList<>((lookupIdentifier(issuerIdCriterion.getIssuerID())));
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    @Nullable public OIDCProviderMetadata resolveSingle(@Nullable final CriteriaSet criteria)
+            throws ResolverException {
+        final Iterable<OIDCProviderMetadata> iterable = resolve(criteria);
+        if (iterable != null) {
+            final Iterator<OIDCProviderMetadata> iterator = iterable.iterator();
+            if (iterator != null && iterator.hasNext()) {
+                return iterator.next();
+            }
+        }
+        log.warn("Could not find any provider metadata with the given criteria");
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected List<OIDCProviderMetadata> parse(final byte[] bytes) throws ParseException {
+        return List.of(OIDCProviderMetadata.parse(JSONObjectUtils.parse(new String(bytes))));
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected Issuer getKey(@Nonnull final OIDCProviderMetadata value) {
+        return value.getIssuer();
+    }
+}
diff --git a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/impl/ReloadingProviderMetadataProvider.java b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/impl/ReloadingProviderMetadataProvider.java
new file mode 100644
index 0000000..9e13bf8
--- /dev/null
+++ b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/impl/ReloadingProviderMetadataProvider.java
@@ -0,0 +1,90 @@
+package net.shibboleth.oidc.metadata.impl;
+
+import java.util.Collections;
+
+import javax.annotation.Nonnull;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
+
+import net.shibboleth.oidc.metadata.ProviderMetadataResolver;
+import net.shibboleth.utilities.java.support.component.AbstractIdentifiableInitializableComponent;
+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;
+import net.shibboleth.utilities.java.support.service.ReloadableService;
+import net.shibboleth.utilities.java.support.service.ServiceableComponent;
+
+
+/**
+ * This class uses the service interface to implement {@link ProviderMetadataResolver}.
+ */
+public class ReloadingProviderMetadataProvider extends AbstractIdentifiableInitializableComponent 
+    implements ProviderMetadataResolver {
+    
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(ReloadingProviderMetadataProvider.class);
+    
+    /** The service which managed the reloading. */
+    private final ReloadableService<ProviderMetadataResolver> service;
+    
+    /**
+     * Constructor.
+     * 
+     * @param resolverService the service which will manage the loading.
+     */
+    public ReloadingProviderMetadataProvider(
+            @Nonnull final ReloadableService<ProviderMetadataResolver> resolverService) {
+        service = Constraint.isNotNull(resolverService, "IssuerMetadataResolver Service cannot be null");
+    }
+
+    @Override
+    public Iterable<OIDCProviderMetadata> resolve(CriteriaSet criteria) throws ResolverException {
+        ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
+        ServiceableComponent<ProviderMetadataResolver> component = null;
+        try {
+            component = service.getServiceableComponent();
+            if (null == component) {
+                log.error("ReloadingIssuerMetadataProvider '{}': Error accessing underlying source: "
+                        + "Invalid configuration.", getId());
+            } else {
+                final ProviderMetadataResolver resolver = component.getComponent();
+                return resolver.resolve(criteria);
+            }
+        } catch (final ResolverException e) {
+            log.error("IssuerMetadataResolver '{}': Error during resolution", getId(), e);
+        } finally {
+            if (null != component) {
+                component.unpinComponent();
+            }
+        }
+        return Collections.emptySet();
+    }
+
+    @Override
+    public OIDCProviderMetadata resolveSingle(CriteriaSet criteria) throws ResolverException {
+        ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
+        ServiceableComponent<ProviderMetadataResolver> component = null;
+        try {
+            component = service.getServiceableComponent();
+            if (null == component) {
+                log.error("ReloadingIssuerMetadataProvider '{}': Error accessing underlying source: "
+                        + "Invalid configuration.", getId());
+            } else {
+                final ProviderMetadataResolver resolver = component.getComponent();
+                return resolver.resolveSingle(criteria);
+            }
+        } catch (final ResolverException e) {
+            log.error("IssuerMetadataResolver '{}': Error during resolution", getId(), e);
+        } finally {
+            if (null != component) {
+                component.unpinComponent();
+            }
+        }
+        return null;
+    }
+
+}
diff --git a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/profile/spring/relyingparty/metadata/impl/ProviderMetadataResolverServiceStrategy.java b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/profile/spring/relyingparty/metadata/impl/ProviderMetadataResolverServiceStrategy.java
new file mode 100644
index 0000000..381c6e9
--- /dev/null
+++ b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/profile/spring/relyingparty/metadata/impl/ProviderMetadataResolverServiceStrategy.java
@@ -0,0 +1,101 @@
+/*
+ * 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.profile.spring.relyingparty.metadata.impl;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.springframework.beans.factory.BeanCreationException;
+import org.springframework.context.ApplicationContext;
+
+import net.shibboleth.oidc.metadata.ProviderMetadataProviderContainer;
+import net.shibboleth.oidc.metadata.ProviderMetadataResolver;
+import net.shibboleth.oidc.metadata.impl.ChainingProviderMetadataResolver;
+import net.shibboleth.utilities.java.support.component.AbstractIdentifiableInitializableComponent;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.resolver.ResolverException;
+import net.shibboleth.utilities.java.support.service.ServiceException;
+import net.shibboleth.utilities.java.support.service.ServiceableComponent;
+
+/**
+ * Strategy for summoning up a {@link ProviderMetadataResolver} from a populated {@link ApplicationContext}.
+ * 
+ * <p>
+ * The logic is the same as in 
+ * net.shibboleth.idp.profile.spring.relyingparty.metadata.impl.MetadataResolverServiceStrategy.
+ * </p>
+ */
+public class ProviderMetadataResolverServiceStrategy extends AbstractIdentifiableInitializableComponent
+        implements Function<ApplicationContext, ServiceableComponent<ProviderMetadataResolver>> {
+
+    /** {@inheritDoc} */
+    @Nullable
+    public ServiceableComponent<ProviderMetadataResolver> apply(@Nonnull final ApplicationContext appContext) {
+        final Collection<ProviderMetadataProviderContainer> containers =
+                appContext.getBeansOfType(ProviderMetadataProviderContainer.class).values();
+
+        if (containers.isEmpty()) {
+            throw new ServiceException(
+                    "Reload did not produce any bean of type " + ProviderMetadataProviderContainer.class.getName());
+        }
+        if (1 == containers.size()) {
+            // done
+            return containers.iterator().next();
+        }
+        
+        // initialize so we can sort
+        for (final ProviderMetadataProviderContainer container : containers) {
+            try {
+                container.initialize();
+            } catch (final ComponentInitializationException e) {
+                throw new BeanCreationException("could not preinitialize , provider metadata provider " 
+                        + container.getId(), e);
+            }
+        }
+        // wrap many containers into a single top level container with a chain of all the individual chains/resolvers.
+        // The result of the strategy needs a single serviceable component.
+        
+        final List<ProviderMetadataProviderContainer> containerList = new ArrayList<>(containers.size());
+        containerList.addAll(containers);
+        Collections.sort(containerList);
+        final ChainingProviderMetadataResolver chain = new ChainingProviderMetadataResolver();
+        try {
+            chain.setResolvers(containerList.stream().
+                    map(ProviderMetadataProviderContainer::getEmbeddedResolver).
+                    collect(Collectors.toList()));
+            chain.setId("MultiFileResolverFor:" + containers.size() + ":Resources");
+            chain.initialize();
+            final ProviderMetadataProviderContainer result = new ProviderMetadataProviderContainer();
+            result.setEmbeddedResolver(chain);
+            result.setApplicationContext(appContext);
+            result.initialize();
+            return result;
+        } catch (final ResolverException | ComponentInitializationException e) {
+            throw new ServiceException("Chaining constructor create failed", e);
+        }
+        
+    }
+
+}
\ No newline at end of file
diff --git a/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/metadata/impl/ChainingProviderMetadataResolverTest.java b/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/metadata/impl/ChainingProviderMetadataResolverTest.java
new file mode 100644
index 0000000..4473813
--- /dev/null
+++ b/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/metadata/impl/ChainingProviderMetadataResolverTest.java
@@ -0,0 +1,59 @@
+package net.shibboleth.oidc.metadata.impl;
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+import org.springframework.core.io.ClassPathResource;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.nimbusds.oauth2.sdk.id.Issuer;
+import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
+
+import net.shibboleth.oidc.metadata.criterion.IssuerIDCriterion;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+import net.shibboleth.utilities.java.support.resolver.ResolverException;
+
+public class ChainingProviderMetadataResolverTest {
+    
+    @Nonnull private ChainingProviderMetadataResolver resolver;
+    
+    @BeforeMethod
+    public void setup() throws ResolverException, IOException, ComponentInitializationException {
+        resolver = new ChainingProviderMetadataResolver();
+        resolver.setId("mockChainResolver");
+        final FilesystemProviderMetadataResolver provider = new FilesystemProviderMetadataResolver(
+                new ClassPathResource("/net/shibboleth/oidc/metadata/impl/openid-configuration.json"));
+        provider.setId("mockFileProvider");
+        provider.initialize();
+        resolver.setResolvers(List.of(provider));
+        resolver.initialize();
+    }
+    
+    @Test public void success() throws ResolverException, IOException {
+   
+        final Issuer issuer = new Issuer("https://op.example.com");
+        final CriteriaSet set = new CriteriaSet(new IssuerIDCriterion(issuer));
+        final Iterator<OIDCProviderMetadata> metadatas = resolver.resolve(set).iterator();
+        
+        Assert.assertEquals(metadatas.next().getIssuer().getValue(), issuer.getValue());
+        Assert.assertFalse(metadatas.hasNext());
+        
+    }
+
+    @Test public void fail() throws ResolverException, IOException {
+       
+        final Issuer issuer = new Issuer("no-metadata");
+        final CriteriaSet set = new CriteriaSet(new IssuerIDCriterion(issuer));
+        final Iterator<OIDCProviderMetadata> metadatas = resolver.resolve(set).iterator();
+
+        Assert.assertFalse(metadatas.hasNext());
+        
+    }
+
+}
diff --git a/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/profile/spring/relyingparty/metadata/impl/ReloadClientResolverServiceConfigurationTest.java b/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/profile/spring/relyingparty/metadata/impl/ReloadClientResolverServiceConfigurationTest.java
index e760cb4..737c10a 100644
--- a/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/profile/spring/relyingparty/metadata/impl/ReloadClientResolverServiceConfigurationTest.java
+++ b/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/profile/spring/relyingparty/metadata/impl/ReloadClientResolverServiceConfigurationTest.java
@@ -44,7 +44,6 @@ import net.shibboleth.idp.profile.testing.RequestContextBuilder;
 import net.shibboleth.oidc.metadata.ClientInformationResolver;
 import net.shibboleth.oidc.metadata.RelyingPartyClientInformationProvider;
 import net.shibboleth.oidc.metadata.impl.ChainingClientInformationResolver;
-import net.shibboleth.oidc.profile.spring.relyingparty.metadata.impl.ClientInformationResolverServiceStrategy;
 import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
 import net.shibboleth.utilities.java.support.component.ComponentSupport;
 import net.shibboleth.utilities.java.support.service.ReloadableService;
diff --git a/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/profile/spring/relyingparty/metadata/impl/ReloadClientResolverServiceConfigurationTest.java b/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/profile/spring/relyingparty/metadata/impl/ReloadProviderMetadataResolverServiceConfigurationTest.java
similarity index 75%
copy from oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/profile/spring/relyingparty/metadata/impl/ReloadClientResolverServiceConfigurationTest.java
copy to oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/profile/spring/relyingparty/metadata/impl/ReloadProviderMetadataResolverServiceConfigurationTest.java
index e760cb4..9adb5dc 100644
--- a/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/profile/spring/relyingparty/metadata/impl/ReloadClientResolverServiceConfigurationTest.java
+++ b/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/profile/spring/relyingparty/metadata/impl/ReloadProviderMetadataResolverServiceConfigurationTest.java
@@ -41,24 +41,20 @@ import net.shibboleth.ext.spring.service.ReloadableSpringService;
 import net.shibboleth.idp.profile.impl.ReloadServiceConfiguration;
 import net.shibboleth.idp.profile.testing.ActionTestingSupport;
 import net.shibboleth.idp.profile.testing.RequestContextBuilder;
-import net.shibboleth.oidc.metadata.ClientInformationResolver;
-import net.shibboleth.oidc.metadata.RelyingPartyClientInformationProvider;
-import net.shibboleth.oidc.metadata.impl.ChainingClientInformationResolver;
-import net.shibboleth.oidc.profile.spring.relyingparty.metadata.impl.ClientInformationResolverServiceStrategy;
+import net.shibboleth.oidc.metadata.ProviderMetadataResolver;
+import net.shibboleth.oidc.metadata.impl.ChainingProviderMetadataResolver;
 import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
 import net.shibboleth.utilities.java.support.component.ComponentSupport;
 import net.shibboleth.utilities.java.support.service.ReloadableService;
 import net.shibboleth.utilities.java.support.service.ServiceableComponent;
 
 /**
- * Unit tests for {@link ClientInformationResolverServiceStrategy}.
- * 
- * Mostly based on <pre>net.shibboleth.idp.profile.spring.relyingparty.metadata.ReloadServiceConfigurationTest</pre>.
+ * Unit tests for {@link ProviderMetadataResolverServiceStrategy}.
  */
-public class ReloadClientResolverServiceConfigurationTest {
+public class ReloadProviderMetadataResolverServiceConfigurationTest {
 
     /** The service. */
-    private ReloadableSpringService<ClientInformationResolver> service;
+    private ReloadableSpringService<ProviderMetadataResolver> service;
 
     private RequestContext src;
     
@@ -68,14 +64,14 @@ public class ReloadClientResolverServiceConfigurationTest {
     
     @BeforeClass public void setup() throws IOException, ComponentInitializationException {
         oneResolver = new ArrayList<>();
-        oneResolver.add(new ClassPathResource("/net/shibboleth/oidc/metadata/impl/oidc-metadata-providers.xml"));
+        oneResolver.add(new ClassPathResource("/net/shibboleth/oidc/metadata/impl/oidc-provider-metadata-providers.xml"));
         
         twoResolvers = new ArrayList<>();
-        twoResolvers.add(new ClassPathResource("/net/shibboleth/oidc/metadata/impl/oidc-metadata-providers2.xml"));
+        twoResolvers.add(new ClassPathResource("/net/shibboleth/oidc/metadata/impl/oidc-provider-metadata-providers2.xml"));
     }
 
     public void setupResolver(final List<Resource> serviceLocations) throws ComponentInitializationException {
-        service = new ReloadableSpringService<>(ClientInformationResolver.class, new ClientInformationResolverServiceStrategy());
+        service = new ReloadableSpringService<>(ProviderMetadataResolver.class, new ProviderMetadataResolverServiceStrategy());
         service.setFailFast(true);
         service.setId("mockId");
         
@@ -92,18 +88,15 @@ public class ReloadClientResolverServiceConfigurationTest {
         final Instant time = service.getLastReloadAttemptInstant();
         service.reload();
         Assert.assertNotEquals(time, service.getLastReloadAttemptInstant());
-        final ServiceableComponent<ClientInformationResolver> component = service.getServiceableComponent();
-        final ClientInformationResolver resolver = component.getComponent();
+        final ServiceableComponent<ProviderMetadataResolver> component = service.getServiceableComponent();
+        final ProviderMetadataResolver resolver = component.getComponent();
         component.unpinComponent();
         Assert.assertEquals(getChainSize(resolver), 1);
     }
     
-    protected int getChainSize(final ClientInformationResolver resolver) {
-        Assert.assertTrue(resolver instanceof RelyingPartyClientInformationProvider);
-        final RelyingPartyClientInformationProvider rpProvider = (RelyingPartyClientInformationProvider) resolver;
-        final ClientInformationResolver embedded = rpProvider.getEmbeddedResolver();
-        Assert.assertTrue(embedded instanceof ChainingClientInformationResolver);
-        final ChainingClientInformationResolver chain = (ChainingClientInformationResolver) embedded;
+    protected int getChainSize(final ProviderMetadataResolver resolver) {
+        Assert.assertTrue(resolver instanceof ChainingProviderMetadataResolver);
+        final ChainingProviderMetadataResolver chain = (ChainingProviderMetadataResolver) resolver;
         return chain.getResolvers().size();
     }
 
@@ -112,8 +105,8 @@ public class ReloadClientResolverServiceConfigurationTest {
         final Instant time = service.getLastReloadAttemptInstant();
         service.reload();
         Assert.assertNotEquals(time, service.getLastReloadAttemptInstant());
-        final ServiceableComponent<ClientInformationResolver> component = service.getServiceableComponent();
-        final ClientInformationResolver resolver = component.getComponent();
+        final ServiceableComponent<ProviderMetadataResolver> component = service.getServiceableComponent();
+        final ProviderMetadataResolver resolver = component.getComponent();
         component.unpinComponent();
         Assert.assertEquals(getChainSize(resolver), 2);
     }
diff --git a/oidc-common-metadata-impl/src/test/resources/net/shibboleth/oidc/metadata/impl/oidc-provider-metadata-providers.xml b/oidc-common-metadata-impl/src/test/resources/net/shibboleth/oidc/metadata/impl/oidc-provider-metadata-providers.xml
new file mode 100644
index 0000000..88d839c
--- /dev/null
+++ b/oidc-common-metadata-impl/src/test/resources/net/shibboleth/oidc/metadata/impl/oidc-provider-metadata-providers.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:context="http://www.springframework.org/schema/context"
+       xmlns:util="http://www.springframework.org/schema/util"
+       xmlns:p="http://www.springframework.org/schema/p"
+       xmlns:c="http://www.springframework.org/schema/c"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
+                           http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
+                           
+       default-init-method="initialize"
+       default-destroy-method="destroy">
+
+    <!-- Generic beans for configuring the OIDC metadata system, in most cases don't need any modifications. -->
+
+     <bean id="shibboleth.oidc.rp.ProviderMetadataProvider" lazy-init="false"
+        class="net.shibboleth.oidc.metadata.ProviderMetadataProviderContainer"
+        p:embeddedResolver-ref="shibboleth.oidc.rp.ChainingProviderMetadataResolver">
+    </bean>
+
+    <bean id="shibboleth.oidc.rp.ChainingProviderMetadataResolver"
+        class="net.shibboleth.oidc.metadata.impl.ChainingProviderMetadataResolver"
+        p:id="InternalEmbeddedChainResolver" 
+        p:resolvers="#{getObject('shibboleth.oidc.rp.ProviderMetadataResolvers')}"/>
+
+    <!-- Generic beans end, in most cases only edit after this line. -->
+
+    <!-- The following example contains one OIDC provider metadata resolvers: reading the OP metadata from a file. -->
+
+    <util:list id="shibboleth.oidc.rp.ProviderMetadataResolvers">
+        <ref bean="ExampleFileResolver" />
+    </util:list>
+
+    <bean id="ExampleFileResolver"
+        class="net.shibboleth.oidc.metadata.impl.FilesystemProviderMetadataResolver" p:id="ExampleFileResolver1"
+        c:metadata="/net/shibboleth/oidc/metadata/impl/openid-configuration.json" />
+    
+</beans>
+
diff --git a/oidc-common-metadata-impl/src/test/resources/net/shibboleth/oidc/metadata/impl/oidc-provider-metadata-providers2.xml b/oidc-common-metadata-impl/src/test/resources/net/shibboleth/oidc/metadata/impl/oidc-provider-metadata-providers2.xml
new file mode 100644
index 0000000..c3887b1
--- /dev/null
+++ b/oidc-common-metadata-impl/src/test/resources/net/shibboleth/oidc/metadata/impl/oidc-provider-metadata-providers2.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+    xmlns:context="http://www.springframework.org/schema/context"
+    xmlns:util="http://www.springframework.org/schema/util" xmlns:p="http://www.springframework.org/schema/p"
+    xmlns:c="http://www.springframework.org/schema/c" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
+                           http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
+
+    default-init-method="initialize" default-destroy-method="destroy">
+
+    <!-- Generic beans for configuring the OIDC metadata system, in most cases don't need any modifications. -->
+    
+     <!-- The following example contains two OIDC provider metadata resolvers: reading the OP metadata from a file
+     in both cases. -->
+
+    <bean id="shibboleth.oidc.rp.ProviderMetadataProvider" lazy-init="false"
+        class="net.shibboleth.oidc.metadata.ProviderMetadataProviderContainer"
+        p:embeddedResolver-ref="shibboleth.oidc.rp.ChainingProviderMetadataResolver">
+    </bean>
+
+    <bean id="shibboleth.oidc.rp.ChainingProviderMetadataResolver"
+        class="net.shibboleth.oidc.metadata.impl.ChainingProviderMetadataResolver" p:id="InternalEmbeddedChainResolver"
+        p:resolvers="#{getObject('shibboleth.oidc.rp.ProviderMetadataResolvers')}" />
+
+    <util:list id="shibboleth.oidc.rp.ProviderMetadataResolvers">
+        <ref bean="ExampleFileResolver" />
+    </util:list>
+
+    <bean id="ExampleFileResolver" class="net.shibboleth.oidc.metadata.impl.FilesystemProviderMetadataResolver"
+        p:id="ExampleFileResolver1" c:metadata="/net/shibboleth/oidc/metadata/impl/openid-configuration.json" />
+
+    <!-- Second set of beans -->
+
+    <bean id="shibboleth.oidc.rp.ProviderMetadataProvider.2" lazy-init="false"
+        class="net.shibboleth.oidc.metadata.ProviderMetadataProviderContainer"
+        p:embeddedResolver-ref="shibboleth.oidc.rp.ChainingProviderMetadataResolver.2">
+    </bean>
+
+    <bean id="shibboleth.oidc.rp.ChainingProviderMetadataResolver.2"
+        class="net.shibboleth.oidc.metadata.impl.ChainingProviderMetadataResolver" p:id="InternalEmbeddedChainResolver"
+        p:resolvers="#{getObject('shibboleth.oidc.rp.ProviderMetadataResolvers.2')}" />
+
+    <util:list id="shibboleth.oidc.rp.ProviderMetadataResolvers.2">
+        <ref bean="ExampleFileResolver.2" />
+    </util:list>
+
+    <bean id="ExampleFileResolver.2" class="net.shibboleth.oidc.metadata.impl.FilesystemProviderMetadataResolver"
+        p:id="ExampleFileResolver2" c:metadata="/net/shibboleth/oidc/metadata/impl/openid-configuration.json" />
+
+   
+
+
+
+</beans>
+
diff --git a/oidc-common-metadata-impl/src/test/resources/net/shibboleth/oidc/metadata/impl/openid-configuration.json b/oidc-common-metadata-impl/src/test/resources/net/shibboleth/oidc/metadata/impl/openid-configuration.json
new file mode 100644
index 0000000..912ff94
--- /dev/null
+++ b/oidc-common-metadata-impl/src/test/resources/net/shibboleth/oidc/metadata/impl/openid-configuration.json
@@ -0,0 +1,55 @@
+{
+"issuer": "https://op.example.com",
+"authorization_endpoint": "https://op.example.com/o/oauth2/v2/auth",
+"token_endpoint": "https://oauth2.op.example.com/token",
+"userinfo_endpoint": "https://openidconnect.op.example.com/v1/userinfo",
+"revocation_endpoint": "https://oauth2.op.example.com/revoke",
+"jwks_uri": "https://op.example.com/oauth2/v3/certs",
+"response_types_supported": [
+"code",
+"token",
+"id_token",
+"code token",
+"code id_token",
+"token id_token",
+"code token id_token",
+"none"
+],
+"subject_types_supported": [
+"public"
+],
+"id_token_signing_alg_values_supported": [
+"RS256"
+],
+"scopes_supported": [
+"openid",
+"email",
+"profile"
+],
+"token_endpoint_auth_methods_supported": [
+"client_secret_post",
+"client_secret_basic"
+],
+"claims_supported": [
+"aud",
+"email",
+"email_verified",
+"exp",
+"family_name",
+"given_name",
+"iat",
+"iss",
+"locale",
+"name",
+"picture",
+"sub"
+],
+"code_challenge_methods_supported": [
+"plain",
+"S256"
+],
+"grant_types_supported": [
+"authorization_code",
+"refresh_token"
+]
+}
\ No newline at end of file

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


More information about the commits mailing list