-
Notifications
You must be signed in to change notification settings - Fork 5.9k
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
Customize RestOperations / WebClient for OAuth 2.0 #8882
Comments
Are there any examples on how to configure a JWTDecoder to use a custom HTTP Client? |
@georgejdli Please see the reference. |
@jgrandja Thanks!
|
Edited by removing parts not relevant to this issue |
@piotrplazienski There are a couple of different issues/enhancements you mentioned that are not directly related to this issue. Please log questions on StackOverflow and new issue(s) for each new feature or enhancement here. |
Hello, We have an issue with the lack of customization for JwtDecoderProviderConfigurationUtils. As far as I can tell, it's currently the only way to deal with OpenID Provider Configuration (spring.security.oauth2.resourceserver.jwt.issuer-uri) which is currently the only way to validate tokens from a big name identity provider who doesn't feel like implementing an introspection endpoint NimbusJwtDecoder only offer introspection and JWK set support which are not implemented in that Identity provider. I haven't found anything related to an extension for issuer-uri in nimbus. So our only solution seem to be to completely rewrite the JwtDecoders to be able to access our issuer-uri through a proxy, customizing timeouts, etc. |
@Choobz There are a few different options available for configuring a Here are a couple of references to jump to: @Bean
JwtDecoder jwtDecoder() {
NimbusJwtDecoder jwtDecoder = (NimbusJwtDecoder)
JwtDecoders.fromIssuerLocation(issuerUri);
OAuth2TokenValidator<Jwt> audienceValidator = audienceValidator();
OAuth2TokenValidator<Jwt> withIssuer = JwtValidators.createDefaultWithIssuer(issuerUri);
OAuth2TokenValidator<Jwt> withAudience = new DelegatingOAuth2TokenValidator<>(withIssuer, audienceValidator);
jwtDecoder.setJwtValidator(withAudience);
return jwtDecoder;
} |
@jgrandja thanks a lot for your answer. Although, our problem still stand unfortunatly :
So in order to implement our own JwtDecoders.fromIssuerLocation that does strictly the same thing but only with a configurable RestOperation (proxy, timeout...) we need to copy/paste 99% of the code of From the list of enhancement, it seems you want to get rid/limit the dependency of spring oauth on nimbus. Which is great but why not implement Pseudo-code : class OAuth2ResourceServerJwtConfiguration {
@Bean
@Conditional(IssuerUriCondition.class)
JwtDecoder jwtDecoderByIssuerUri() {
// Default to virgin RestTemplate
return JwtDecoders.fromIssuerLocation(this.properties.getIssuerUri()).restOperation(new RestTemplate()).build();
}
...
public final class JwtDecoders {
public IssuerBuilder fromIssuerLocation(String issuer) {
return new IssuerBuilder().fromIssuerLocation(issuer);
}
public final class IssuerBuilder {
private RestOperation rest;
private String issuer;
public JwtDecoderBuilder fromIssuerLocation(String issuer) {
Assert.hasText(issuer, "issuer cannot be empty");
this.issuer=issuer;
return this;
}
public JwtDecoderBuilder restOperations(RestOperation rest) {
Assert.notNull(rest, "restcannot be null");
this.rest=rest;
return this;
}
public <T extends JwtDecoder> build(){
Map<String, Object> configuration = JwtDecoderProviderConfigurationUtils
.getConfigurationForIssuerLocation(issuer, rest);
return (T) withProviderConfiguration(configuration, issuer);
}
}
}
... (oc its only a quick hacky way of doing it, there's a lot to improve) So that from a client perspective it becomes quite easy to use : @Bean
public JwtDecoder jwtDecoder(RestTemplate whatever) {
return JwtDecoders
.fromIssuerLocation(issuerUri)
.restOperations(whatever)
.build();
} I'm probably missing something obvious because it's real shame to set aside all the work on the inbuilt issuerDecoder and reinvent the wheel in our codebases just to be able to parametrized the RestTemplate used to retrieve the configuration in the .well-known endpoint :(. |
Due to a recent change in the nimbus jose jwt library 1, this issue can become critical:
In our case, this completely blocked all request processing threads and the service hat to be restartet. At the very least, the connect/read/write timeouts should be set to sensible defaults for versions of spring-security-oauth2-jose that pull in the affected version of nimbus-jose-jwt (>= 9.16.1). |
Regrettably, the solution provided by @jgrandja didn't work. Everything we found online, we tried programmatically and failed epically! We tried to use However, we still get We found that the the only thing that works so far is to set the VM options using flags Does anyone have any other solution that we can try to make this work behind a proxy white connected to a VPN? |
This is the part that fails in This is the piece that fails:
static Map<String, JwkDefinitionHolder> loadJwkDefinitions(URL jwkSetUrl) {
InputStream jwkSetSource;
try {
jwkSetSource = jwkSetUrl.openStream();
} catch (IOException ex) {
throw new JwkException("An I/O error occurred while reading from the JWK Set source: " + ex.getMessage(), ex);
} Normally, to setup the proxy you would do something like this: Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("10.0.0.1", 8080));
conn = new URL(urlString).openConnection(proxy); However, the System.setProperty("https.proxyHost", "internet.ford.com")
System.setProperty("https.proxyPort", "83")
System.setProperty("http.proxyHost", "internet.ford.com")
System.setProperty("http.proxyPort", "83")
System.setProperty("http.nonProxyHosts", "*.ford.com|localhost")
System.setProperty("https.nonProxyHosts", "*.ford.com|localhost") or from the command line with the -D syntax Which is super painful!!! |
Getting this running behind a proxy took a lot of effort and I'm still not 100% sure exactly why it now works - we are not using a vpn but it is worth a try We ended up registering a JwtDecoderFactory rather than a JwtDecoder itself (Kotlin)
where restOperations is
Alternatively using a ProxySelector worked but also affects any other apps etc in the same JVM - Tomcat in our case
and initialising it early in app startup
|
Hey @mallen, at least you provide me with some code that I can play and experiment with and I appreciate that. Thank you. |
Thanks everyone for so much valuable input on this issue! We have introduced simplified configuration via gh-11783 that allows for configuring a
Note also the related issue for adding general support for With that in mind, I'm finally going to close this issue. If anyone has additional requests, please open a new enhancement. |
We've been working on an enhancement (gh-8732) that allows an application to provide a custom
RestOperations
orWebClient
@Bean
, which would be auto-wired to the related components foroauth2-client
oroauth2-resource-server
. Unfortunately, we ran into a few challenges while trying to come up with a solution for this enhancement. Below are the details outlining the issues we faced.There are 2 options available for auto-wiring:
Auto-wire by type
Configuration Scenario 1
If the application context does not contain a
@Bean
of typeRestOperations
(orWebClient
) then this solution will work. The application would register the customized@Bean
and it will be auto-wired into the related components foroauth2-client
oroauth2-resource-server
.Configuration Scenario 2
If the application context already contains one or more
@Bean
of typeRestOperations
(orWebClient
) then this solution will not work. The only way to distinguish which@Bean
to use in the related components foroauth2-client
oroauth2-resource-server
is if the@Bean
is marked as@Primary
. However, if there is already a@Bean
marked as@Primary
, then this is not a viable option either unless the application changes the existing@Primary
@Bean
.Based on this analysis, auto-wiring by type is NOT a viable solution, since it will not work for ALL configuration scenarios.
Auto-wire by bean name
Assuming Spring Security reserves the
@Bean
nameoauth2ClientRestOperations
and the application registers a@Bean
with that name, then it would be auto-wired into the relatedoauth2-client
components. This seemed like a viable solution, however, as we investigated this further, we discovered various configuration scenarios that may become an issue if we went down this path.The main issue with this solution, is that we would need to reserve the following
@Bean
names:oauth2ClientRestOperations
-oauth2-client
Servletoauth2ClientWebClient
-oauth2-client
WebFluxoauth2ResourceServerRestOperations
-oauth2-resource-server
Servletoauth2ResourceServerWebClient
-oauth2-resource-server
WebFluxReserving these 4
@Bean
names is not ideal as we foresee possible issues that may arise by using this bean name strategy. For example, if a Servlet-based application is configured as anoauth2-resource-server
andoauth2-client
(acting as a client), and it needs to customize theRestOperations
, then it would need to register theoauth2ClientRestOperations
andoauth2ResourceServerRestOperations
@Bean
. But what if the customizedRestOperations
could be shared betweenoauth2-client
andoauth2-resource-server
? The application would have to register theRestOperations
@Bean
twice under the 2 distinct names, however, this should not be a requirement.We did consider using a coarse grained bean naming strategy, eg.
oauth2RestOperations
orspringSecurityRestOperations
, but we also foresee similar issues that may arise here as well.Based on this analysis, auto-wiring by name is NOT a viable solution either, since it may introduce issues as described above and we're not 100% confident that the bean naming strategy will work for all possible configuration scenarios.
Motivation for this enhancement
The motivation for this enhancement was initially logged in gh-5607.
Issue gh-7027 and gh-8365 are also related, as the goal is to allow for customizing the underlying HTTP client (
RestOperations
orWebClient
).Based on our analysis and the issues we discovered as described above, it looks like we will NOT be providing this enhancement after all. However, we are not closing the door on this yet, as we would like to gather feedback from the community before we make the final decision.
Having said that, there is still a need for an application to configure a custom
RestOperations
orWebClient
(eg. Proxy, TLS, etc.) for the related components inoauth2-client
oroauth2-resource-server
. The following sample configurations will demonstrate how to do so.OAuth 2.0 Client (Servlet) #
The
oauth2-client
components that allow for a customRestOperations
are:DefaultAuthorizationCodeTokenResponseClient
DefaultRefreshTokenTokenResponseClient
DefaultClientCredentialsTokenResponseClient
DefaultPasswordTokenResponseClient
DefaultOAuth2UserService
The following configuration could be applied to
HttpSecurity.oauth2Login()
that provides a customRestOperations
:If the application also requires the use of
refresh_token
,client_credentials
andpassword
authorization grants, then the following configuration should also be applied:OAuth 2.0 Client (WebFlux)
The
oauth2-client
reactive components that allow for a customWebClient
are:WebClientReactiveAuthorizationCodeTokenResponseClient
WebClientReactiveRefreshTokenTokenResponseClient
WebClientReactiveClientCredentialsTokenResponseClient
WebClientReactivePasswordTokenResponseClient
DefaultReactiveOAuth2UserService
ServerHttpSecurity.oauth2Login()
provides the same configuration options asHttpSecurity.oauth2Login()
so the same configuration could be applied as described forHttpSecurity.oauth2Login()
.OAuth 2.0 Resource Server (Servlet) #
The
oauth2-resource-server
components that allow for a customRestOperations
are:NimbusJwtDecoder
NimbusOpaqueTokenIntrospector
See the reference on how to configure
NimbusJwtDecoder
with a customRestOperations
.See the reference on how to configure
NimbusOpaqueTokenIntrospector
with a customRestOperations
.OAuth 2.0 Resource Server (WebFlux)
The
oauth2-resource-server
reactive components that allow for a customWebClient
are:NimbusReactiveJwtDecoder
NimbusReactiveOpaqueTokenIntrospector
See the Servlet reference on how to configure
NimbusReactiveJwtDecoder
with a customWebClient
, as the configuration would be very similar.See the Servlet reference on how to configure a
NimbusReactiveOpaqueTokenIntrospector
with a customWebClient
, as the configuration would be very similar.ClientRegistrations #
Related gh-7027 gh-5543
ClientRegistrations
is intended to be used as a utility/convenience class. It was designed to fulfill most use cases, however, it may not be suitable for certain use cases. For example, if the internal network traffic must be routed through a Proxy, you can bypass discovery by configuring theauthorization-uri
andtoken-uri
property instead of theissuer-uri
property.NOTE: The underlying HTTP Client used in
ClientRegistrations
was purposely encapsulated and there is no plan to expose it.JwtDecoders \ ReactiveJwtDecoders #
Related gh-8365 gh-5543
JwtDecoders
andReactiveJwtDecoders
are both intended to be used as a utility/convenience class. It was designed to fulfill most use cases, however, it may not be suitable for certain use cases. For example, if the underlying HTTP Client requires Proxy and/or TLS settings, you can configure aJwtDecoder
orReactiveJwtDecoder
with the custom HTTP Client and expose it as a@Bean
.The reference provides sample configuration on how to configure a custom
JwtDecoder
orReactiveJwtDecoder
@Bean
. See example 1 and example 2.NOTE: The underlying HTTP Client used in
JwtDecoders
andReactiveJwtDecoders
was purposely encapsulated and there is no plan to expose it.The text was updated successfully, but these errors were encountered: