[java-oidc-common] branch main updated: Allow passing the KeyId from the criterion to the remote JWK Set cache
Phil Smart
philip.smart at jisc.ac.uk
Thu Jan 5 13:55:56 UTC 2023
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=d140549a2b49a8fe2f1d8f1b363cb57295fc3462
The following commit(s) were added to refs/heads/main by this push:
new d140549 Allow passing the KeyId from the criterion to the remote JWK Set cache
d140549 is described below
commit d140549a2b49a8fe2f1d8f1b363cb57295fc3462
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Thu Jan 5 13:55:53 2023 +0000
Allow passing the KeyId from the criterion to the remote JWK Set cache
- This helps find keys during key roll over
---
.../impl/ProviderMetadataCredentialResolver.java | 41 +++++++++++++++++++---
.../ProviderMetadataCredentialResolverTest.java | 31 ++++++++++++++++
...er-resolver-remote-jwkset-response-rollover.jwk | 11 ++++++
3 files changed, 78 insertions(+), 5 deletions(-)
diff --git a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/credential/impl/ProviderMetadataCredentialResolver.java b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/credential/impl/ProviderMetadataCredentialResolver.java
index 9244d4b..2f50a10 100644
--- a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/credential/impl/ProviderMetadataCredentialResolver.java
+++ b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/credential/impl/ProviderMetadataCredentialResolver.java
@@ -24,6 +24,7 @@ import java.util.Collections;
import java.util.LinkedHashSet;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import org.opensaml.security.credential.Credential;
import org.opensaml.security.credential.impl.AbstractCriteriaFilteringCredentialResolver;
@@ -38,6 +39,7 @@ 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.criterion.KeyIdCriterion;
import net.shibboleth.oidc.security.criterion.ProviderMetadataCriterion;
import net.shibboleth.oidc.security.impl.CredentialConversionUtil;
import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
@@ -46,6 +48,7 @@ import net.shibboleth.utilities.java.support.component.ComponentInitializationEx
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.primitive.StringSupport;
import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
import net.shibboleth.utilities.java.support.resolver.ResolverException;
@@ -137,9 +140,18 @@ public class ProviderMetadataCredentialResolver extends BasicJOSEObjectCredentia
final LinkedHashSet<Credential> credentials = new LinkedHashSet<>(1);
- if (metadata.getJWKSetURI() != null) {
- final JWKSet keySet = remoteJwkSetCache.fetch(metadata.getJWKSetURI(),
- Instant.now().plus(keyFetchInterval));
+ if (metadata.getJWKSetURI() != null) {
+
+ final String keyIdFromCriteria = extractKeyIdFromCriteria(criteriaSet);
+
+ JWKSet keySet = null;
+ if (StringSupport.trimOrNull(keyIdFromCriteria) != null) {
+ keySet = remoteJwkSetCache.fetch(metadata.getJWKSetURI(),keyIdFromCriteria,
+ Instant.now().plus(keyFetchInterval));
+ } else {
+ keySet = remoteJwkSetCache.fetch(metadata.getJWKSetURI(),
+ Instant.now().plus(keyFetchInterval));
+ }
if (keySet == null) {
log.debug("Remote keys could not be fetched, unable to resolve credentials");
@@ -165,7 +177,26 @@ public class ProviderMetadataCredentialResolver extends BasicJOSEObjectCredentia
} else {
log.trace("No JWK Set available, no credentials returned");
}
- return credentials;
-
+ return credentials;
+ }
+
+ /**
+ * Extract a KeyId from the criteria set if one exists. If not, return {@code null}.
+ *
+ * @param criteriaSet the criteria set to pull the keyId from
+ *
+ * @return a KeyId if one exists, {@code null} otherwise
+ */
+ @Nullable private String extractKeyIdFromCriteria(@Nonnull final CriteriaSet criteriaSet) {
+ if (criteriaSet.contains(EvaluableKeyIDCredentialCriterion.class)) {
+ return criteriaSet.get(EvaluableKeyIDCredentialCriterion.class).getKeyId();
+ } else if (criteriaSet.contains(KeyIdCriterion.class)) {
+ return criteriaSet.get(KeyIdCriterion.class).getKeyId();
+ } else {
+ return null;
+ }
+
}
+
+
}
diff --git a/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/credential/impl/ProviderMetadataCredentialResolverTest.java b/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/credential/impl/ProviderMetadataCredentialResolverTest.java
index 1ca8e86..439ea40 100644
--- a/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/credential/impl/ProviderMetadataCredentialResolverTest.java
+++ b/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/credential/impl/ProviderMetadataCredentialResolverTest.java
@@ -24,7 +24,10 @@ import static org.testng.Assert.fail;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
+import java.net.URI;
import java.nio.charset.StandardCharsets;
+import java.time.Duration;
+import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
@@ -52,6 +55,7 @@ import org.testng.annotations.Test;
import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
import net.shibboleth.oidc.jwk.RemoteJwkSetCache;
+import net.shibboleth.oidc.security.criterion.KeyIdCriterion;
import net.shibboleth.oidc.security.criterion.ProviderMetadataCriterion;
import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
@@ -69,6 +73,10 @@ public class ProviderMetadataCredentialResolverTest {
private static final ClassPathResource REMOTE_JWKSET_NO_KEYS =
new ClassPathResource("/credentials/test-provider-resolver-remote-jwkset-response-nokeys.jwk");
+ /** A remote JWKSet with a rolled over key.*/
+ private static final ClassPathResource REMOTE_JWKSET_ROLLOVER =
+ new ClassPathResource("/credentials/test-provider-resolver-remote-jwkset-response-rollover.jwk");
+
/** A remote JWKSet with no keys array.*/
private static final ClassPathResource REMOTE_JWKSET_NO_KEY_ARRAY =
new ClassPathResource("/credentials/test-provider-resolver-remote-jwkset-response-no-key-array.jwk");
@@ -186,6 +194,29 @@ public class ProviderMetadataCredentialResolverTest {
assertEquals(credsList.size(), 0);
}
+ /* Test refetching the keyset if the keyset is cached but the key ID did not exist.*/
+ @Test
+ public void testSuccess_KeyRotatedRefetch() throws Exception {
+ // Cache the keyset, then fetch a new keyID later
+ cache.fetch(new URI("https://localhost:9921/oauth2/v3/certs"),
+ Instant.now().plus(Duration.ofMinutes(10)));
+ resolver.initialize();
+
+ //Roll over the keyset document
+ cache.setHttpClient(createMockHttpClient(readJsonFromFile(REMOTE_JWKSET_ROLLOVER)));
+
+ criteria.add(new UsageCriterion(UsageType.SIGNING));
+ criteria.add(new EvaluableKeyIDCredentialCriterion(new KeyIdCriterion("not-in-original-cache")));
+
+ final Iterable<Credential> creds = resolver.resolve(criteria);
+
+ assertNotNull(creds);
+ final List<Credential> credsList = new ArrayList<>();
+ creds.forEach(credsList::add);
+ // A key
+ assertEquals(credsList.size(), 1);
+ }
+
@Test
public void testSuccess_ForEncryptionAndKeyAlg() throws Exception {
resolver.initialize();
diff --git a/oidc-common-crypto-impl/src/test/resources/credentials/test-provider-resolver-remote-jwkset-response-rollover.jwk b/oidc-common-crypto-impl/src/test/resources/credentials/test-provider-resolver-remote-jwkset-response-rollover.jwk
new file mode 100644
index 0000000..f2a69de
--- /dev/null
+++ b/oidc-common-crypto-impl/src/test/resources/credentials/test-provider-resolver-remote-jwkset-response-rollover.jwk
@@ -0,0 +1,11 @@
+{
+"keys": [
+{
+"kty": "RSA",
+"e": "AQAB",
+"use": "sig",
+"kid": "not-in-original-cache",
+"n": "kk-3jeBmUPbpMk0fEdIn-APAdNOoOckA0e-SiALLxy5dWfG-GyF51g31zuM_iNiSiMSsmG2ZAVi48iItFpd-JW9IIT40TC147I6aKrel0Rf39Mwp-1tCzME6VYEgOmgI9qDg2e4edt1cvjQfiw3IZlXakwgYQn2BuoknoCBVjETVLHrnsvEqXhPffzML9O5Ze_nBOX6-pCAzVsimr-ljoln2GQz-ID5fGzlflXJV78v7QzlyyAAQovYQMxiEBgecHu44S0Iu_esLEOOobQkZyHc-OgcwEazfJUEUhKEnevVTJFlQF3Odxp1I6W9zd-zLUceqIMKF5Xs10AfmkPhboQ"
+}
+]
+}
\ 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