Affects: \
If this is not the right place for this issue, please let me know.
There is a specific issue with Spring Security and Spring WebFlux's WebClient in general, where it is not that easy to understand how to pass the authentication from the Spring Framework MVC controller directly into WebClient, if the project does not use OAuth2Authentication
, and when the Spring Security is configured so that the endpoints must require authority (ROLE_ etc.).
The username and password can be obtained from the Authentication
object passed into the Spring controller method argument. However, the password is BCrypt-encoded and is not viewable in plain text form.
Here's a code example:
@RequestMapping (value = "/whatever")
public String passthroughToSpringBootActuator(Authentication auth, HttpServletRequest request, HttpSession session) {
//Do some magic with the user-authorized authentication.
String baseUrl = request.getRequestUrl().toString().replace(request.getRequestUri().toString(), "");
WebClient webClient = WebClient.builder().baseUrl(baseUrl).build();
String json = webClient
.get()
.uri(baseUrl + "/actuator")
//Do some voodoo magic here that sets the "Authorization" token with the user-authorized authentication from above
.headers(headers -> ...)
.retrieve().bodyToMono(String.class).block();
//Do stuffs with the JSON response.
//...
return "myFoo.page";
}
The purpose of the code above is to invoke a REST GET request to a Spring Boot Actuator to get JSON data back, and doing so while Spring Security made the actuator endpoints all requiring the user having some authority (roles). It is simple as that.
On the line .headers(headers -> ...)
, the WebClient headers
can invoke these methods, setBasicAuth(...)
, and setBearerToken(...)
. But none of these methods allow the developer to create a valid basic authentication header token if the password is BCrypt-encoded.
I wanted to request for an enhancement, to allow a direct way of setting the Authorization
request header with something that can be obtained from the Authentication
object that is passed in as the argument of the Spring controller method. That way, the Spring WebFlux WebClient can send a REST request to an endpoint secured by Spring Security, with little complexity involved.
Otherwise, the current scope of only obtaining the JSON response data from an endpoint (or Spring Boot Actuator) secured by Spring Security would explode in complexity, all because Spring Security requires the endpoint to be accessible only to those with the proper authorities/roles.
Or, we can set the endpoints to be permitted by all in Spring Security, but this will compromise the integrity of the security around those endpoints.
I do not see a way to pass Authentication
object or derived objects from Authentication
, to WebClient to get through to the endpoint through Spring Security, while the user's password credential is BCrypt-encoded,
Comment From: bclozel
I'm not sure I understand here.
To summarize, you'd like to get the credentials of the authenticated user in plaintext form to reuse them in the WebClient
and make a request to a remote service using those credentials?
Comment From: tommai78101
Not plain text. We wanted to try and avoid using plain text if possible.
In short, I wanted a feature to allow us to reuse the Authentication
data from the Spring controller's method argument, and pass it into WebClient
so that we can set some data (either by using the Authentication
object itself, or we can use some object we obtained from the Authentication
getter methods) to be able to make passthroughs viable across backend communications.
The data can be of anything useful for verifying/authenticating the session, such as a bearer's token, or some kind of secret token. After all, we are able to set the token in the WebClient
request header (like the bearer's token).
Comment From: ttddyy
@tommai78101
I wanted to request for an enhancement, to allow a direct way of setting the Authorization request header with something that can be obtained from the Authentication object that is passed in as the argument of the Spring controller method. That way, the Spring WebFlux WebClient can send a REST request to an endpoint secured by Spring Security, with little complexity involved.
Isn't it simply get the Authorization
header from HttpRequest
and set it to WebClient
header?
Can you simply use HttpServletRequest
instead of Authentication
object??
Also, if you want to add such behavior globally, you could write an ExchangeFilterFunction
and add it to WebClient.Builder
in a WebClientCustomizer
bean. Then, inject Spring Boot managed WebClient.Builder
bean where you want make remote calls. Here is the Spring Boot documentation how to customize WebClient
.
Comment From: bclozel
It looks like you're trying to set up an authentication/authorization architecture that's similar but not identical to OAuth2. Your first comment seems to indicate that you're not considering OAuth2 for this case. Depending on what you're trying to achieve, OAuth2 login/client might solve this. Spring Security does support this with WebFlux so it seems that all the required pieces to create such an architecture are in place in Spring Framework.
In other words, the current issue seems to be about many things:
- an authentication/authorization architecture that's not really defined, but not OAuth2. Spring Framework does not offer opinions on this and Spring Security should be the place to look for correct, secure implementations
- extension mechanisms to implement authentication/authorization on top of WebClient in Spring Framework. I believe we do already provide such mechanisms
- extending from
Authentication
or providing a custom mechanism - you can already do that, since Spring Security is doing so. I don't think rolling your own, custom authentication implementation is a good idea
I'm closing this issue since we're mixing a lot of concerns here and most of them aren't about Spring Framework. Please have a look at Spring Security and try using/customizing its features first. If a particular customization mechanism is missing in Security or Framework, we can then consider that in a different issue.
Thanks
Comment From: tommai78101
From what I can tell, to use OAuth2, you require extra dependencies to generate those tokens, and usually it would be other libraries such as JSON Web Tokens (JWT). That is what I meant as "additional complexity". I'm not sure if there's a way for Authentication
to pass around OAuth2 tokens from the Spring controller method arguments.
I'll see what I can find in Spring Security and how to extend the Authentication
with additional mechanisms. If you know where else I can look, feel free to point me towards those directions. Thanks.