Skip to content

Provide custom WebClient and RestOperations to ReactiveJwtDecoders and JwtDecoders #17698

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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 <a href=
Expand Down Expand Up @@ -52,10 +53,29 @@ private JwtDecoders() {
*/
@SuppressWarnings("unchecked")
public static <T extends JwtDecoder> T fromOidcIssuerLocation(String oidcIssuerLocation) {
return fromOidcIssuerLocation(oidcIssuerLocation, null);
}

/**
* Creates a {@link JwtDecoder} using the provided <a href=
* "https://openid.net/specs/openid-connect-core-1_0.html#IssuerIdentifier">Issuer</a>
* by making an <a href=
* "https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationRequest">OpenID
* Provider Configuration Request</a> and using the values in the <a href=
* "https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse">OpenID
* Provider Configuration Response</a> to initialize the {@link JwtDecoder}.
* @param oidcIssuerLocation the <a href=
* "https://openid.net/specs/openid-connect-core-1_0.html#IssuerIdentifier">Issuer</a>
* @param restOperations customized {@link RestOperations}
* @return a {@link JwtDecoder} that was initialized by the OpenID Provider
* Configuration.
*/
public static <T extends JwtDecoder> T fromOidcIssuerLocation(String oidcIssuerLocation,
RestOperations restOperations) {
Assert.hasText(oidcIssuerLocation, "oidcIssuerLocation cannot be empty");
Map<String, Object> configuration = JwtDecoderProviderConfigurationUtils
.getConfigurationForOidcIssuerLocation(oidcIssuerLocation);
return (T) withProviderConfiguration(configuration, oidcIssuerLocation);
return (T) withProviderConfiguration(configuration, oidcIssuerLocation, restOperations);
}

/**
Expand Down Expand Up @@ -88,8 +108,46 @@ public static <T extends JwtDecoder> T fromOidcIssuerLocation(String oidcIssuerL
*/
@SuppressWarnings("unchecked")
public static <T extends JwtDecoder> T fromIssuerLocation(String issuer) {
return fromIssuerLocation(issuer, null);
}

/**
* Creates a {@link JwtDecoder} using the provided <a href=
* "https://openid.net/specs/openid-connect-core-1_0.html#IssuerIdentifier">Issuer</a>
* 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}:
*
* <ol>
* <li>{@code host/.well-known/openid-configuration/path}, as defined in
* <a href="https://tools.ietf.org/html/rfc8414#section-5">RFC 8414's Compatibility
* Notes</a>.</li>
* <li>{@code issuer/.well-known/openid-configuration}, as defined in <a href=
* "https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationRequest">
* OpenID Provider Configuration</a>.</li>
* <li>{@code host/.well-known/oauth-authorization-server/path}, as defined in
* <a href="https://tools.ietf.org/html/rfc8414#section-3.1">Authorization Server
* Metadata Request</a>.</li>
* </ol>
*
* Note that the second endpoint is the equivalent of calling
* {@link JwtDecoders#fromOidcIssuerLocation(String)}
* @param issuer the <a href=
* "https://openid.net/specs/openid-connect-core-1_0.html#IssuerIdentifier">Issuer</a>
* @param restOperations customized {@link RestOperations}
* @return a {@link JwtDecoder} that was initialized by one of the described endpoints
*/
@SuppressWarnings("unchecked")
public static <T extends JwtDecoder> 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<Jwt> jwtValidator = JwtValidators.createDefaultWithIssuer(issuer);
jwtDecoder.setJwtValidator(jwtValidator);
return (T) jwtDecoder;
Expand All @@ -104,15 +162,20 @@ public static <T extends JwtDecoder> T fromIssuerLocation(String issuer) {
* @param configuration the configuration values
* @param issuer the <a href=
* "https://openid.net/specs/openid-connect-core-1_0.html#IssuerIdentifier">Issuer</a>
* @param restOperations customized {@link RestOperations}
* @return {@link JwtDecoder}
*/
private static JwtDecoder withProviderConfiguration(Map<String, Object> configuration, String issuer) {
private static JwtDecoder withProviderConfiguration(Map<String, Object> configuration, String issuer,
RestOperations restOperations) {
JwtDecoderProviderConfigurationUtils.validateIssuer(configuration, issuer);
OAuth2TokenValidator<Jwt> 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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 <a href=
Expand Down Expand Up @@ -51,10 +52,29 @@ private ReactiveJwtDecoders() {
*/
@SuppressWarnings("unchecked")
public static <T extends ReactiveJwtDecoder> T fromOidcIssuerLocation(String oidcIssuerLocation) {
return fromOidcIssuerLocation(oidcIssuerLocation, null);
}

/**
* Creates a {@link ReactiveJwtDecoder} using the provided <a href=
* "https://openid.net/specs/openid-connect-core-1_0.html#IssuerIdentifier">Issuer</a>
* by making an <a href=
* "https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationRequest">OpenID
* Provider Configuration Request</a> and using the values in the <a href=
* "https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse">OpenID
* Provider Configuration Response</a> to initialize the {@link ReactiveJwtDecoder}.
* @param oidcIssuerLocation the <a href=
* "https://openid.net/specs/openid-connect-core-1_0.html#IssuerIdentifier">Issuer</a>
* @param webClient customized {@link WebClient}
* @return a {@link ReactiveJwtDecoder} that was initialized by the OpenID Provider
* Configuration.
*/
public static <T extends ReactiveJwtDecoder> T fromOidcIssuerLocation(String oidcIssuerLocation,
WebClient webClient) {
Assert.hasText(oidcIssuerLocation, "oidcIssuerLocation cannot be empty");
Map<String, Object> configuration = JwtDecoderProviderConfigurationUtils
.getConfigurationForOidcIssuerLocation(oidcIssuerLocation);
return (T) withProviderConfiguration(configuration, oidcIssuerLocation);
return (T) withProviderConfiguration(configuration, oidcIssuerLocation, webClient);
}

/**
Expand Down Expand Up @@ -88,10 +108,45 @@ public static <T extends ReactiveJwtDecoder> T fromOidcIssuerLocation(String oid
*/
@SuppressWarnings("unchecked")
public static <T extends ReactiveJwtDecoder> T fromIssuerLocation(String issuer) {
return fromIssuerLocation(issuer, null);
}

/**
* Creates a {@link ReactiveJwtDecoder} using the provided <a href=
* "https://openid.net/specs/openid-connect-core-1_0.html#IssuerIdentifier">Issuer</a>
* 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}:
*
* <ol>
* <li>{@code host/.well-known/openid-configuration/path}, as defined in
* <a href="https://tools.ietf.org/html/rfc8414#section-5">RFC 8414's Compatibility
* Notes</a>.</li>
* <li>{@code issuer/.well-known/openid-configuration}, as defined in <a href=
* "https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationRequest">
* OpenID Provider Configuration</a>.</li>
* <li>{@code host/.well-known/oauth-authorization-server/path}, as defined in
* <a href="https://tools.ietf.org/html/rfc8414#section-3.1">Authorization Server
* Metadata Request</a>.</li>
* </ol>
*
* Note that the second endpoint is the equivalent of calling
* {@link ReactiveJwtDecoders#fromOidcIssuerLocation(String)}
* @param issuer the <a href=
* "https://openid.net/specs/openid-connect-core-1_0.html#IssuerIdentifier">Issuer</a>
* @param webClient customized {@link WebClient}
* @return a {@link ReactiveJwtDecoder} that was initialized by one of the described
* endpoints
*/
@SuppressWarnings("unchecked")
public static <T extends ReactiveJwtDecoder> T fromIssuerLocation(String issuer, WebClient webClient) {
Assert.hasText(issuer, "issuer cannot be empty");
Map<String, Object> configuration = JwtDecoderProviderConfigurationUtils
.getConfigurationForIssuerLocation(issuer);
return (T) withProviderConfiguration(configuration, issuer);
return (T) withProviderConfiguration(configuration, issuer, webClient);
}

/**
Expand All @@ -103,15 +158,21 @@ public static <T extends ReactiveJwtDecoder> T fromIssuerLocation(String issuer)
* @param configuration the configuration values
* @param issuer the <a href=
* "https://openid.net/specs/openid-connect-core-1_0.html#IssuerIdentifier">Issuer</a>
* @param webClient customized {@link WebClient}
* @return {@link ReactiveJwtDecoder}
*/
private static ReactiveJwtDecoder withProviderConfiguration(Map<String, Object> configuration, String issuer) {
private static ReactiveJwtDecoder withProviderConfiguration(Map<String, Object> configuration, String issuer,
WebClient webClient) {
JwtDecoderProviderConfigurationUtils.validateIssuer(configuration, issuer);
OAuth2TokenValidator<Jwt> 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;
}
Expand Down