Skip to content

Commit ac76fcd

Browse files
Introduced custom WebClient and RestOperations to ReactiveJwtDecords and JwtDecoders
Signed-off-by: Thiago Locatelli <[email protected]>
1 parent 6f1232c commit ac76fcd

File tree

2 files changed

+138
-12
lines changed

2 files changed

+138
-12
lines changed

oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtDecoders.java

Lines changed: 70 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020

2121
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
2222
import org.springframework.util.Assert;
23+
import org.springframework.web.client.RestOperations;
24+
2325

2426
/**
2527
* Allows creating a {@link JwtDecoder} from an <a href=
@@ -52,10 +54,29 @@ private JwtDecoders() {
5254
*/
5355
@SuppressWarnings("unchecked")
5456
public static <T extends JwtDecoder> T fromOidcIssuerLocation(String oidcIssuerLocation) {
57+
return fromOidcIssuerLocation(oidcIssuerLocation, null);
58+
}
59+
60+
/**
61+
* Creates a {@link JwtDecoder} using the provided <a href=
62+
* "https://openid.net/specs/openid-connect-core-1_0.html#IssuerIdentifier">Issuer</a>
63+
* by making an <a href=
64+
* "https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationRequest">OpenID
65+
* Provider Configuration Request</a> and using the values in the <a href=
66+
* "https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse">OpenID
67+
* Provider Configuration Response</a> to initialize the {@link JwtDecoder}.
68+
* @param oidcIssuerLocation the <a href=
69+
* "https://openid.net/specs/openid-connect-core-1_0.html#IssuerIdentifier">Issuer</a>
70+
* @param restOperations customized {@link RestOperations}
71+
* @return a {@link JwtDecoder} that was initialized by the OpenID Provider
72+
* Configuration.
73+
*/
74+
public static <T extends JwtDecoder> T fromOidcIssuerLocation(String oidcIssuerLocation,
75+
RestOperations restOperations) {
5576
Assert.hasText(oidcIssuerLocation, "oidcIssuerLocation cannot be empty");
5677
Map<String, Object> configuration = JwtDecoderProviderConfigurationUtils
5778
.getConfigurationForOidcIssuerLocation(oidcIssuerLocation);
58-
return (T) withProviderConfiguration(configuration, oidcIssuerLocation);
79+
return (T) withProviderConfiguration(configuration, oidcIssuerLocation, restOperations);
5980
}
6081

6182
/**
@@ -88,8 +109,46 @@ public static <T extends JwtDecoder> T fromOidcIssuerLocation(String oidcIssuerL
88109
*/
89110
@SuppressWarnings("unchecked")
90111
public static <T extends JwtDecoder> T fromIssuerLocation(String issuer) {
112+
return fromIssuerLocation(issuer, null);
113+
}
114+
115+
/**
116+
* Creates a {@link JwtDecoder} using the provided <a href=
117+
* "https://openid.net/specs/openid-connect-core-1_0.html#IssuerIdentifier">Issuer</a>
118+
* by querying three different discovery endpoints serially, using the values in the
119+
* first successful response to initialize. If an endpoint returns anything other than
120+
* a 200 or a 4xx, the method will exit without attempting subsequent endpoints.
121+
*
122+
* The three endpoints are computed as follows, given that the {@code issuer} is
123+
* composed of a {@code host} and a {@code path}:
124+
*
125+
* <ol>
126+
* <li>{@code host/.well-known/openid-configuration/path}, as defined in
127+
* <a href="https://tools.ietf.org/html/rfc8414#section-5">RFC 8414's Compatibility
128+
* Notes</a>.</li>
129+
* <li>{@code issuer/.well-known/openid-configuration}, as defined in <a href=
130+
* "https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationRequest">
131+
* OpenID Provider Configuration</a>.</li>
132+
* <li>{@code host/.well-known/oauth-authorization-server/path}, as defined in
133+
* <a href="https://tools.ietf.org/html/rfc8414#section-3.1">Authorization Server
134+
* Metadata Request</a>.</li>
135+
* </ol>
136+
*
137+
* Note that the second endpoint is the equivalent of calling
138+
* {@link JwtDecoders#fromOidcIssuerLocation(String)}
139+
* @param issuer the <a href=
140+
* "https://openid.net/specs/openid-connect-core-1_0.html#IssuerIdentifier">Issuer</a>
141+
* @param restOperations customized {@link RestOperations}
142+
* @return a {@link JwtDecoder} that was initialized by one of the described endpoints
143+
*/
144+
@SuppressWarnings("unchecked")
145+
public static <T extends JwtDecoder> T fromIssuerLocation(String issuer, RestOperations restOperations) {
91146
Assert.hasText(issuer, "issuer cannot be empty");
92-
NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(issuer).build();
147+
NimbusJwtDecoder.JwkSetUriJwtDecoderBuilder builder = NimbusJwtDecoder.withIssuerLocation(issuer);
148+
if (restOperations != null) {
149+
builder = builder.restOperations(restOperations);
150+
}
151+
NimbusJwtDecoder jwtDecoder = builder.build();
93152
OAuth2TokenValidator<Jwt> jwtValidator = JwtValidators.createDefaultWithIssuer(issuer);
94153
jwtDecoder.setJwtValidator(jwtValidator);
95154
return (T) jwtDecoder;
@@ -104,15 +163,20 @@ public static <T extends JwtDecoder> T fromIssuerLocation(String issuer) {
104163
* @param configuration the configuration values
105164
* @param issuer the <a href=
106165
* "https://openid.net/specs/openid-connect-core-1_0.html#IssuerIdentifier">Issuer</a>
166+
* @param restOperations customized {@link RestOperations}
107167
* @return {@link JwtDecoder}
108168
*/
109-
private static JwtDecoder withProviderConfiguration(Map<String, Object> configuration, String issuer) {
169+
private static JwtDecoder withProviderConfiguration(Map<String, Object> configuration, String issuer,
170+
RestOperations restOperations) {
110171
JwtDecoderProviderConfigurationUtils.validateIssuer(configuration, issuer);
111172
OAuth2TokenValidator<Jwt> jwtValidator = JwtValidators.createDefaultWithIssuer(issuer);
112173
String jwkSetUri = configuration.get("jwks_uri").toString();
113-
NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withJwkSetUri(jwkSetUri)
114-
.jwtProcessorCustomizer(JwtDecoderProviderConfigurationUtils::addJWSAlgorithms)
115-
.build();
174+
NimbusJwtDecoder.JwkSetUriJwtDecoderBuilder builder = NimbusJwtDecoder.withJwkSetUri(jwkSetUri)
175+
.jwtProcessorCustomizer(JwtDecoderProviderConfigurationUtils::addJWSAlgorithms);
176+
if (restOperations != null) {
177+
builder = builder.restOperations(restOperations);
178+
}
179+
NimbusJwtDecoder jwtDecoder = builder.build();
116180
jwtDecoder.setJwtValidator(jwtValidator);
117181
return jwtDecoder;
118182
}

oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/ReactiveJwtDecoders.java

Lines changed: 68 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020

2121
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
2222
import org.springframework.util.Assert;
23+
import org.springframework.web.reactive.function.client.WebClient;
24+
2325

2426
/**
2527
* Allows creating a {@link ReactiveJwtDecoder} from an <a href=
@@ -51,10 +53,29 @@ private ReactiveJwtDecoders() {
5153
*/
5254
@SuppressWarnings("unchecked")
5355
public static <T extends ReactiveJwtDecoder> T fromOidcIssuerLocation(String oidcIssuerLocation) {
56+
return fromOidcIssuerLocation(oidcIssuerLocation, null);
57+
}
58+
59+
/**
60+
* Creates a {@link ReactiveJwtDecoder} using the provided <a href=
61+
* "https://openid.net/specs/openid-connect-core-1_0.html#IssuerIdentifier">Issuer</a>
62+
* by making an <a href=
63+
* "https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationRequest">OpenID
64+
* Provider Configuration Request</a> and using the values in the <a href=
65+
* "https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse">OpenID
66+
* Provider Configuration Response</a> to initialize the {@link ReactiveJwtDecoder}.
67+
* @param oidcIssuerLocation the <a href=
68+
* "https://openid.net/specs/openid-connect-core-1_0.html#IssuerIdentifier">Issuer</a>
69+
* @param webClient customized {@link WebClient}
70+
* @return a {@link ReactiveJwtDecoder} that was initialized by the OpenID Provider
71+
* Configuration.
72+
*/
73+
public static <T extends ReactiveJwtDecoder> T fromOidcIssuerLocation(String oidcIssuerLocation,
74+
WebClient webClient) {
5475
Assert.hasText(oidcIssuerLocation, "oidcIssuerLocation cannot be empty");
5576
Map<String, Object> configuration = JwtDecoderProviderConfigurationUtils
5677
.getConfigurationForOidcIssuerLocation(oidcIssuerLocation);
57-
return (T) withProviderConfiguration(configuration, oidcIssuerLocation);
78+
return (T) withProviderConfiguration(configuration, oidcIssuerLocation, webClient);
5879
}
5980

6081
/**
@@ -88,10 +109,45 @@ public static <T extends ReactiveJwtDecoder> T fromOidcIssuerLocation(String oid
88109
*/
89110
@SuppressWarnings("unchecked")
90111
public static <T extends ReactiveJwtDecoder> T fromIssuerLocation(String issuer) {
112+
return fromIssuerLocation(issuer, null);
113+
}
114+
115+
/**
116+
* Creates a {@link ReactiveJwtDecoder} using the provided <a href=
117+
* "https://openid.net/specs/openid-connect-core-1_0.html#IssuerIdentifier">Issuer</a>
118+
* by querying three different discovery endpoints serially, using the values in the
119+
* first successful response to initialize. If an endpoint returns anything other than
120+
* a 200 or a 4xx, the method will exit without attempting subsequent endpoints.
121+
*
122+
* The three endpoints are computed as follows, given that the {@code issuer} is
123+
* composed of a {@code host} and a {@code path}:
124+
*
125+
* <ol>
126+
* <li>{@code host/.well-known/openid-configuration/path}, as defined in
127+
* <a href="https://tools.ietf.org/html/rfc8414#section-5">RFC 8414's Compatibility
128+
* Notes</a>.</li>
129+
* <li>{@code issuer/.well-known/openid-configuration}, as defined in <a href=
130+
* "https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationRequest">
131+
* OpenID Provider Configuration</a>.</li>
132+
* <li>{@code host/.well-known/oauth-authorization-server/path}, as defined in
133+
* <a href="https://tools.ietf.org/html/rfc8414#section-3.1">Authorization Server
134+
* Metadata Request</a>.</li>
135+
* </ol>
136+
*
137+
* Note that the second endpoint is the equivalent of calling
138+
* {@link ReactiveJwtDecoders#fromOidcIssuerLocation(String)}
139+
* @param issuer the <a href=
140+
* "https://openid.net/specs/openid-connect-core-1_0.html#IssuerIdentifier">Issuer</a>
141+
* @param webClient customized {@link WebClient}
142+
* @return a {@link ReactiveJwtDecoder} that was initialized by one of the described
143+
* endpoints
144+
*/
145+
@SuppressWarnings("unchecked")
146+
public static <T extends ReactiveJwtDecoder> T fromIssuerLocation(String issuer, WebClient webClient) {
91147
Assert.hasText(issuer, "issuer cannot be empty");
92148
Map<String, Object> configuration = JwtDecoderProviderConfigurationUtils
93149
.getConfigurationForIssuerLocation(issuer);
94-
return (T) withProviderConfiguration(configuration, issuer);
150+
return (T) withProviderConfiguration(configuration, issuer, webClient);
95151
}
96152

97153
/**
@@ -103,15 +159,21 @@ public static <T extends ReactiveJwtDecoder> T fromIssuerLocation(String issuer)
103159
* @param configuration the configuration values
104160
* @param issuer the <a href=
105161
* "https://openid.net/specs/openid-connect-core-1_0.html#IssuerIdentifier">Issuer</a>
162+
* @param webClient customized {@link WebClient}
106163
* @return {@link ReactiveJwtDecoder}
107164
*/
108-
private static ReactiveJwtDecoder withProviderConfiguration(Map<String, Object> configuration, String issuer) {
165+
private static ReactiveJwtDecoder withProviderConfiguration(Map<String, Object> configuration, String issuer,
166+
WebClient webClient) {
109167
JwtDecoderProviderConfigurationUtils.validateIssuer(configuration, issuer);
110168
OAuth2TokenValidator<Jwt> jwtValidator = JwtValidators.createDefaultWithIssuer(issuer);
111169
String jwkSetUri = configuration.get("jwks_uri").toString();
112-
NimbusReactiveJwtDecoder jwtDecoder = NimbusReactiveJwtDecoder.withJwkSetUri(jwkSetUri)
113-
.jwtProcessorCustomizer(ReactiveJwtDecoderProviderConfigurationUtils::addJWSAlgorithms)
114-
.build();
170+
NimbusReactiveJwtDecoder.JwkSetUriReactiveJwtDecoderBuilder builder = NimbusReactiveJwtDecoder
171+
.withJwkSetUri(jwkSetUri)
172+
.jwtProcessorCustomizer(ReactiveJwtDecoderProviderConfigurationUtils::addJWSAlgorithms);
173+
if (webClient != null) {
174+
builder = builder.webClient(webClient);
175+
}
176+
NimbusReactiveJwtDecoder jwtDecoder = builder.build();
115177
jwtDecoder.setJwtValidator(jwtValidator);
116178
return jwtDecoder;
117179
}

0 commit comments

Comments
 (0)