diff --git a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtDecoders.java b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtDecoders.java
index 295bfb8a98..cb83524fcb 100644
--- a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtDecoders.java
+++ b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtDecoders.java
@@ -20,6 +20,7 @@
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
import org.springframework.util.Assert;
+import org.springframework.web.client.RestOperations;
/**
* Allows creating a {@link JwtDecoder} from an T fromOidcIssuerLocation(String oidcIssuerLocation) {
+ return fromOidcIssuerLocation(oidcIssuerLocation, null);
+ }
+
+ /**
+ * Creates a {@link JwtDecoder} using the provided Issuer
+ * by making an OpenID
+ * Provider Configuration Request and using the values in the OpenID
+ * Provider Configuration Response to initialize the {@link JwtDecoder}.
+ * @param oidcIssuerLocation the Issuer
+ * @param restOperations customized {@link RestOperations}
+ * @return a {@link JwtDecoder} that was initialized by the OpenID Provider
+ * Configuration.
+ */
+ public static T fromOidcIssuerLocation(String oidcIssuerLocation,
+ RestOperations restOperations) {
Assert.hasText(oidcIssuerLocation, "oidcIssuerLocation cannot be empty");
Map configuration = JwtDecoderProviderConfigurationUtils
.getConfigurationForOidcIssuerLocation(oidcIssuerLocation);
- return (T) withProviderConfiguration(configuration, oidcIssuerLocation);
+ return (T) withProviderConfiguration(configuration, oidcIssuerLocation, restOperations);
}
/**
@@ -88,8 +108,46 @@ public static T fromOidcIssuerLocation(String oidcIssuerL
*/
@SuppressWarnings("unchecked")
public static T fromIssuerLocation(String issuer) {
+ return fromIssuerLocation(issuer, null);
+ }
+
+ /**
+ * Creates a {@link JwtDecoder} using the provided Issuer
+ * by querying three different discovery endpoints serially, using the values in the
+ * first successful response to initialize. If an endpoint returns anything other than
+ * a 200 or a 4xx, the method will exit without attempting subsequent endpoints.
+ *
+ * The three endpoints are computed as follows, given that the {@code issuer} is
+ * composed of a {@code host} and a {@code path}:
+ *
+ *
+ * - {@code host/.well-known/openid-configuration/path}, as defined in
+ * RFC 8414's Compatibility
+ * Notes.
+ * - {@code issuer/.well-known/openid-configuration}, as defined in
+ * OpenID Provider Configuration.
+ * - {@code host/.well-known/oauth-authorization-server/path}, as defined in
+ * Authorization Server
+ * Metadata Request.
+ *
+ *
+ * Note that the second endpoint is the equivalent of calling
+ * {@link JwtDecoders#fromOidcIssuerLocation(String)}
+ * @param issuer the Issuer
+ * @param restOperations customized {@link RestOperations}
+ * @return a {@link JwtDecoder} that was initialized by one of the described endpoints
+ */
+ @SuppressWarnings("unchecked")
+ public static T fromIssuerLocation(String issuer, RestOperations restOperations) {
Assert.hasText(issuer, "issuer cannot be empty");
- NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(issuer).build();
+ NimbusJwtDecoder.JwkSetUriJwtDecoderBuilder builder = NimbusJwtDecoder.withIssuerLocation(issuer);
+ if (restOperations != null) {
+ builder = builder.restOperations(restOperations);
+ }
+ NimbusJwtDecoder jwtDecoder = builder.build();
OAuth2TokenValidator jwtValidator = JwtValidators.createDefaultWithIssuer(issuer);
jwtDecoder.setJwtValidator(jwtValidator);
return (T) jwtDecoder;
@@ -104,15 +162,20 @@ public static T fromIssuerLocation(String issuer) {
* @param configuration the configuration values
* @param issuer the Issuer
+ * @param restOperations customized {@link RestOperations}
* @return {@link JwtDecoder}
*/
- private static JwtDecoder withProviderConfiguration(Map configuration, String issuer) {
+ private static JwtDecoder withProviderConfiguration(Map configuration, String issuer,
+ RestOperations restOperations) {
JwtDecoderProviderConfigurationUtils.validateIssuer(configuration, issuer);
OAuth2TokenValidator jwtValidator = JwtValidators.createDefaultWithIssuer(issuer);
String jwkSetUri = configuration.get("jwks_uri").toString();
- NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withJwkSetUri(jwkSetUri)
- .jwtProcessorCustomizer(JwtDecoderProviderConfigurationUtils::addJWSAlgorithms)
- .build();
+ NimbusJwtDecoder.JwkSetUriJwtDecoderBuilder builder = NimbusJwtDecoder.withJwkSetUri(jwkSetUri)
+ .jwtProcessorCustomizer(JwtDecoderProviderConfigurationUtils::addJWSAlgorithms);
+ if (restOperations != null) {
+ builder = builder.restOperations(restOperations);
+ }
+ NimbusJwtDecoder jwtDecoder = builder.build();
jwtDecoder.setJwtValidator(jwtValidator);
return jwtDecoder;
}
diff --git a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/ReactiveJwtDecoders.java b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/ReactiveJwtDecoders.java
index b6d37496c5..6b7976f062 100644
--- a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/ReactiveJwtDecoders.java
+++ b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/ReactiveJwtDecoders.java
@@ -20,6 +20,7 @@
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
import org.springframework.util.Assert;
+import org.springframework.web.reactive.function.client.WebClient;
/**
* Allows creating a {@link ReactiveJwtDecoder} from an T fromOidcIssuerLocation(String oidcIssuerLocation) {
+ return fromOidcIssuerLocation(oidcIssuerLocation, null);
+ }
+
+ /**
+ * Creates a {@link ReactiveJwtDecoder} using the provided Issuer
+ * by making an OpenID
+ * Provider Configuration Request and using the values in the OpenID
+ * Provider Configuration Response to initialize the {@link ReactiveJwtDecoder}.
+ * @param oidcIssuerLocation the Issuer
+ * @param webClient customized {@link WebClient}
+ * @return a {@link ReactiveJwtDecoder} that was initialized by the OpenID Provider
+ * Configuration.
+ */
+ public static T fromOidcIssuerLocation(String oidcIssuerLocation,
+ WebClient webClient) {
Assert.hasText(oidcIssuerLocation, "oidcIssuerLocation cannot be empty");
Map configuration = JwtDecoderProviderConfigurationUtils
.getConfigurationForOidcIssuerLocation(oidcIssuerLocation);
- return (T) withProviderConfiguration(configuration, oidcIssuerLocation);
+ return (T) withProviderConfiguration(configuration, oidcIssuerLocation, webClient);
}
/**
@@ -88,10 +108,45 @@ public static T fromOidcIssuerLocation(String oid
*/
@SuppressWarnings("unchecked")
public static T fromIssuerLocation(String issuer) {
+ return fromIssuerLocation(issuer, null);
+ }
+
+ /**
+ * Creates a {@link ReactiveJwtDecoder} using the provided Issuer
+ * by querying three different discovery endpoints serially, using the values in the
+ * first successful response to initialize. If an endpoint returns anything other than
+ * a 200 or a 4xx, the method will exit without attempting subsequent endpoints.
+ *
+ * The three endpoints are computed as follows, given that the {@code issuer} is
+ * composed of a {@code host} and a {@code path}:
+ *
+ *
+ * - {@code host/.well-known/openid-configuration/path}, as defined in
+ * RFC 8414's Compatibility
+ * Notes.
+ * - {@code issuer/.well-known/openid-configuration}, as defined in
+ * OpenID Provider Configuration.
+ * - {@code host/.well-known/oauth-authorization-server/path}, as defined in
+ * Authorization Server
+ * Metadata Request.
+ *
+ *
+ * Note that the second endpoint is the equivalent of calling
+ * {@link ReactiveJwtDecoders#fromOidcIssuerLocation(String)}
+ * @param issuer the Issuer
+ * @param webClient customized {@link WebClient}
+ * @return a {@link ReactiveJwtDecoder} that was initialized by one of the described
+ * endpoints
+ */
+ @SuppressWarnings("unchecked")
+ public static T fromIssuerLocation(String issuer, WebClient webClient) {
Assert.hasText(issuer, "issuer cannot be empty");
Map configuration = JwtDecoderProviderConfigurationUtils
.getConfigurationForIssuerLocation(issuer);
- return (T) withProviderConfiguration(configuration, issuer);
+ return (T) withProviderConfiguration(configuration, issuer, webClient);
}
/**
@@ -103,15 +158,21 @@ public static T fromIssuerLocation(String issuer)
* @param configuration the configuration values
* @param issuer the Issuer
+ * @param webClient customized {@link WebClient}
* @return {@link ReactiveJwtDecoder}
*/
- private static ReactiveJwtDecoder withProviderConfiguration(Map configuration, String issuer) {
+ private static ReactiveJwtDecoder withProviderConfiguration(Map configuration, String issuer,
+ WebClient webClient) {
JwtDecoderProviderConfigurationUtils.validateIssuer(configuration, issuer);
OAuth2TokenValidator jwtValidator = JwtValidators.createDefaultWithIssuer(issuer);
String jwkSetUri = configuration.get("jwks_uri").toString();
- NimbusReactiveJwtDecoder jwtDecoder = NimbusReactiveJwtDecoder.withJwkSetUri(jwkSetUri)
- .jwtProcessorCustomizer(ReactiveJwtDecoderProviderConfigurationUtils::addJWSAlgorithms)
- .build();
+ NimbusReactiveJwtDecoder.JwkSetUriReactiveJwtDecoderBuilder builder = NimbusReactiveJwtDecoder
+ .withJwkSetUri(jwkSetUri)
+ .jwtProcessorCustomizer(ReactiveJwtDecoderProviderConfigurationUtils::addJWSAlgorithms);
+ if (webClient != null) {
+ builder = builder.webClient(webClient);
+ }
+ NimbusReactiveJwtDecoder jwtDecoder = builder.build();
jwtDecoder.setJwtValidator(jwtValidator);
return jwtDecoder;
}