diff --git a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/NimbusJwtDecoder.java b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/NimbusJwtDecoder.java
index eb5efbabecb..eacb6664d32 100644
--- a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/NimbusJwtDecoder.java
+++ b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/NimbusJwtDecoder.java
@@ -261,6 +261,16 @@ public static SecretKeyJwtDecoderBuilder withSecretKey(SecretKey secretKey) {
return new SecretKeyJwtDecoderBuilder(secretKey);
}
+ /**
+ * Use the given JWK Set
+ * uri.
+ * @param jwkSetUri the JWK Set uri to use
+ * @return a {@link JwkSetUriJwtDecoderBuilder} for further configurations
+ */
+ public static JwkSetUriJwtDecoderBuilder withJwkSource(JWKSource jwkSetUri) {
+ return new JwkSetUriJwtDecoderBuilder(jwkSetUri);
+ }
+
/**
* A builder for creating {@link NimbusJwtDecoder} instances based on a
* JWK Set
@@ -274,7 +284,7 @@ public static final class JwkSetUriJwtDecoderBuilder {
private static final JOSEObjectTypeVerifier NO_TYPE_VERIFIER = (header, context) -> {
};
- private final Function jwkSetUri;
+ private Function jwkSetUri;
private Function, Set> defaultAlgorithms = (source) -> Set
.of(JWSAlgorithm.RS256);
@@ -289,6 +299,8 @@ public static final class JwkSetUriJwtDecoderBuilder {
private Consumer> jwtProcessorCustomizer;
+ private JWKSource jwkSource;
+
private JwkSetUriJwtDecoderBuilder(String jwkSetUri) {
Assert.hasText(jwkSetUri, "jwkSetUri cannot be empty");
this.jwkSetUri = (rest) -> jwkSetUri;
@@ -306,6 +318,13 @@ private JwkSetUriJwtDecoderBuilder(Function jwkSetUri,
};
}
+ private JwkSetUriJwtDecoderBuilder(JWKSource jwkSource) {
+ Assert.notNull(jwkSource, "jwkSource cannot be null");
+ this.jwkSource = jwkSource;
+ this.jwtProcessorCustomizer = (processor) -> {
+ };
+ }
+
/**
* Whether to use Nimbus's typ header verification. This is {@code true} by
* default, however it may change to {@code false} in a future major release.
@@ -436,6 +455,9 @@ JWSKeySelector jwsKeySelector(JWKSource jwkSou
}
JWKSource jwkSource() {
+ if (this.jwkSource != null) {
+ return this.jwkSource;
+ }
String jwkSetUri = this.jwkSetUri.apply(this.restOperations);
return JWKSourceBuilder.create(new SpringJWKSource<>(this.restOperations, this.cache, jwkSetUri))
.refreshAheadCache(false)
diff --git a/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/NimbusJwtDecoderTests.java b/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/NimbusJwtDecoderTests.java
index 9b7805a0d89..f0ba7196de4 100644
--- a/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/NimbusJwtDecoderTests.java
+++ b/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/NimbusJwtDecoderTests.java
@@ -42,6 +42,7 @@
import com.nimbusds.jose.JWSSigner;
import com.nimbusds.jose.crypto.MACSigner;
import com.nimbusds.jose.crypto.RSASSASigner;
+import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.BadJOSEException;
import com.nimbusds.jose.proc.DefaultJOSEObjectTypeVerifier;
@@ -559,6 +560,22 @@ public void decodeWhenUsingSecretKeyWithKidThenStillUsesKey() throws Exception {
// @formatter:on
}
+ // gh-7056
+ @Test
+ public void decodeWhenUsingJwkSource() throws Exception {
+ JWKSource source = (a, b) -> {
+ try {
+ return JWKSet.parse(JWK_SET).getKeys();
+ }
+ catch (ParseException ex) {
+ throw new RuntimeException(ex);
+ }
+ };
+ NimbusJwtDecoder decoder = NimbusJwtDecoder.withJwkSource(source).build();
+ Jwt jwt = decoder.decode(SIGNED_JWT);
+ assertThat(jwt.getClaimAsString("sub")).isEqualTo("test-subject");
+ }
+
// gh-8730
@Test
public void withSecretKeyWhenUsingCustomTypeHeaderThenSuccessfullyDecodes() throws Exception {