-
Notifications
You must be signed in to change notification settings - Fork 6k
Introduce OAuth2AuthorizedClient Manager/Provider #6845
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
Introduce OAuth2AuthorizedClient Manager/Provider #6845
Conversation
.../main/java/org/springframework/security/oauth2/client/OAuth2ClientAuthorizationProvider.java
Outdated
Show resolved
Hide resolved
.../main/java/org/springframework/security/oauth2/client/OAuth2ClientAuthorizationProvider.java
Outdated
Show resolved
Hide resolved
.../springframework/security/oauth2/client/endpoint/DefaultRefreshTokenTokenResponseClient.java
Show resolved
Hide resolved
It's worth asking ourselves...are we trying to just look up an access token or combine it with a lookup of the current access token with resolving it if it isn't found. Another concern I have with the current APIs is that method inputs are things like To help us understand what is going to be of the most use to our users, I'd like the PR to include some sample usage of the API (in services not just controllers) too. |
The The flow is triggered by the input to |
Can we put together a sample of its usage? Also demo what our internal classes will look like when consuming this API? I'd really like to see this driven by the usage and a firm understanding of how the user will interact with the API. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks great, @jgrandja. I especially like how this has the same feel as authentication providers.
I've left some feedback inline.
...client/web/reactive/function/client/ServletOAuth2AuthorizedClientExchangeFilterFunction.java
Outdated
Show resolved
Hide resolved
...client/web/reactive/function/client/ServletOAuth2AuthorizedClientExchangeFilterFunction.java
Outdated
Show resolved
Hide resolved
ebee74f
to
359bbeb
Compare
7c393dd
to
5ab9d67
Compare
5ab9d67
to
eef15bc
Compare
...ent/src/main/java/org/springframework/security/oauth2/client/OAuth2AuthorizationContext.java
Outdated
Show resolved
Hide resolved
...ent/src/main/java/org/springframework/security/oauth2/client/OAuth2AuthorizationContext.java
Outdated
Show resolved
Hide resolved
...ent/src/main/java/org/springframework/security/oauth2/client/OAuth2AuthorizationContext.java
Outdated
Show resolved
Hide resolved
...a/org/springframework/security/oauth2/client/RefreshTokenOAuth2AuthorizedClientProvider.java
Outdated
Show resolved
Hide resolved
...ent/src/main/java/org/springframework/security/oauth2/client/OAuth2AuthorizationContext.java
Show resolved
Hide resolved
...a/org/springframework/security/oauth2/client/RefreshTokenOAuth2AuthorizedClientProvider.java
Outdated
Show resolved
Hide resolved
...ork/security/oauth2/client/web/method/annotation/OAuth2AuthorizedClientArgumentResolver.java
Outdated
Show resolved
Hide resolved
...src/main/java/org/springframework/security/oauth2/client/OAuth2AuthorizedClientProvider.java
Show resolved
Hide resolved
…edClient" This reverts commit dd6c5b0.
…izedClient - spring-projects#59 Redesign OAuth2AuthorizedClientProvider to load/save OAuth2AuthorizedClient - spring-projects#60 ClientCredentialsOAuth2AuthorizedClientProvider should load/save OAuth2AuthorizedClient - spring-projects#61 RefreshTokenOAuth2AuthorizedClientProvider should load/save OAuth2AuthorizedClient - spring-projects#62 Refactor and use redesigned OAuth2AuthorizedClientProvider implementations
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the updates.
I'm not a fan with the fact that we need to look up all the information multiple times. For example, AuthorizationCodeOAuth2AuthorizedClientProvider
can look up the ClientRegistration and the OAuth2AuthorizedClient. ClientCredentialsOAuth2AuthorizedClientProvider can also look up the ClientRegistration and OAuth2AuthorizedClient. Nearly every implementation of OAuth2AuthorizedClientProvider
has the potential to look up duplicate information which is going to add a lot of unnecessary overhead.
Another thing I question is "Why are these split up into separate implementations to begin with?" It seems that the information for what a user wants is already going to be provided. For example, if the grant type is client credentials they probably want to support that mechanism. They probably don't want to ensure that they have created the DelegatingOAuth2AuthorizedClientProvider properly.
It might be nice to support situations where pieces of this information is already known (i.e. we already have a ClientRegistration or OAuth2AuthorizedClient) without needing to look up additional information.
- spring-projects#73 Introduce OAuth2AuthorizedClientManager - spring-projects#74 Integrate OAuth2AuthorizedClientManager with OAuth2AuthorizedClientProvider(s) - spring-projects#81 Add builder for OAuth2AuthorizedClientProvider
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the updates. I have replied inline
.../main/java/org/springframework/security/oauth2/client/web/OAuth2AuthorizedClientManager.java
Outdated
Show resolved
Hide resolved
.../main/java/org/springframework/security/oauth2/client/web/OAuth2AuthorizedClientManager.java
Outdated
Show resolved
Hide resolved
...client/web/reactive/function/client/ServletOAuth2AuthorizedClientExchangeFilterFunction.java
Outdated
Show resolved
Hide resolved
...client/web/reactive/function/client/ServletOAuth2AuthorizedClientExchangeFilterFunction.java
Show resolved
Hide resolved
.../springframework/security/oauth2/client/AuthorizationCodeOAuth2AuthorizedClientProvider.java
Outdated
Show resolved
Hide resolved
.../springframework/security/oauth2/client/ClientCredentialsOAuth2AuthorizedClientProvider.java
Outdated
Show resolved
Hide resolved
...ork/security/oauth2/client/web/method/annotation/OAuth2AuthorizedClientArgumentResolver.java
Outdated
Show resolved
Hide resolved
...client/web/reactive/function/client/ServletOAuth2AuthorizedClientExchangeFilterFunction.java
Show resolved
Hide resolved
.../springframework/security/oauth2/client/ClientCredentialsOAuth2AuthorizedClientProvider.java
Outdated
Show resolved
Hide resolved
private ClientRegistration clientRegistration; | ||
private OAuth2AuthorizedClient authorizedClient; | ||
private Authentication principal; | ||
private Map<String, Object> attributes; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm still curious how this is used in practice without custom code. At the moment, DefaultOAuth2AuthorizedClientManager doesn't add any attributes. Perhaps it is better to wait on such functionality (including the DefaultOAuth2AuthorizedClientManager.contextAttributesMapper).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I'll remove from this PR and add contextAttributesMapper
back in for password
grant PR, as that's where it will be used initially
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to clarify from my last comment, I will remove DefaultOAuth2AuthorizedClientManager.contextAttributesMapper
but will not remove OAuth2AuthorizationContext.attributes
as it's being used by RefreshTokenOAuth2AuthorizedClientProvider
. You can see the usage in this test
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rwinch There indeed was a gap in my initial implementation of DefaultOAuth2AuthorizedClientManager
. There now is a default implementation of contextAttributesMapper
that provides the support for RefreshTokenOAuth2AuthorizedClientProvider
and very possibly future implementations of OAuth2AuthorizedClientProvider
(e.g. dynamic scopes). Please see this test for usage.
* @see OAuth2AuthorizeRequest | ||
* @see OAuth2AuthorizedClientManager | ||
*/ | ||
public class OAuth2ReauthorizeRequest extends OAuth2AuthorizeRequest { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it makes sense for OAuth2ReauthorizeRequest
to extend OAuth2AuthorizeRequest
. What does it mean for OAuth2ReauthorizeRequest
to be passed into OAuth2AuthorizedClientManager.authorize
? The types should enforce correct usage.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it makes sense for
OAuth2ReauthorizeRequest
to extendOAuth2AuthorizeRequest
This makes perfect sense to me. A request to re-authorize, (e.g. token is expired) is considered an authorization request as well. Therefore, a re-authorization request is an authorization request.
What does it mean for
OAuth2ReauthorizeRequest
to be passed intoOAuth2AuthorizedClientManager.authorize()
Why would this even happen? The API is clear authorize(OAuth2AuthorizeRequest)
so even if a OAuth2ReauthorizeRequest
was passed into authorize()
it would only be used as an OAuth2AuthorizeRequest
and never cast to an OAuth2ReauthorizeRequest
.
Can you expand on the use case because as far as I see it the user would not be using the API correctly in this instance if they were to pass in an OAuth2ReauthorizeRequest
, but the method implementation would be correctly implemented based on the clear definition of the operation and it's input.
Also, the added benefit of having the hierarchy OAuth2ReauthorizeRequest
extends OAuth2AuthorizeRequest
is the DefaultOAuth2AuthorizedClientManager
will only need one setContextAttributesMapper(Function<OAuth2AuthorizeRequest, Map<String, Object>> contextAttributesMapper)
. It would not be ideal to have a second setContextAttributesMapper(Function<OAuth2ReauthorizeRequest, Map<String, Object>> contextAttributesMapper)
(typed OAuth2ReauthorizeRequest
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The API should enforce proper usage. It is surprising behavior that the API allows an
OAuth2AuthorizedClient to be provided to authorize via the OAuth2ReauthorizeRequest but it will be completely ignored.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd like to hear your answer regarding my question...
Why would this even happen?...
Is this really a valid scenario? Like I already mentioned this would be a user error on the usage of the API. But the API would still behave as expected, ignoring the OAuth2ReauthorizeRequest
attributes.
Furthermore, based on this understanding does this issue become invalid? See comment. Because based on this issue, an OAuth2LoginAuthenticationToken
could get passed into an OAuth2AuthorizationCodeAuthenticationProvider
which is identically scenario we are discussing here. @jzheaux Let's get your input here as I'm still not convinced.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this really a valid scenario?
That is exactly my point. The API should not allow this to happen, but it does.
But the API would still behave as expected,
As a user I find this confusing. If I create an OAuth2ReauthorizeRequest
providing the OAuth2AuthorizedClient
, it is surprising to me that the OAuth2AuthorizedClient
would be ignored.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a need for the two types and the separate methods? Looking at the default implementations, there seems to be a large overlap. If both are indeed authorize requests, as the hierarchy implies, perhaps OAuth2AuthorizeRequest
could optionally hold an OAuth2AuthorizedClient
- would this remove the need for reauthorize
and OAuth2ReauthorizeRequest
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I personally like the clarity of the API for OAuth2AuthorizedClientManager
having both authorize()
and reauthorize()
. However, I'm open to discussing the removal of reauthorize()
. If we were to do that than we have this:
public interface OAuth2AuthorizedClientManager {
@Nullable
OAuth2AuthorizedClient authorize(OAuth2AuthorizeRequest authorizeRequest);
}
public class OAuth2AuthorizeRequest {
private final String clientRegistrationId;
private final OAuth2AuthorizedClient authorizedClient;
private final Authentication principal;
private final HttpServletRequest servletRequest;
private final HttpServletResponse servletResponse;
public OAuth2AuthorizeRequest(String clientRegistrationId, Authentication principal,
HttpServletRequest servletRequest, HttpServletResponse servletResponse) {
}
public OAuth2AuthorizeRequest(OAuth2AuthorizedClient authorizedClient, Authentication principal,
HttpServletRequest servletRequest, HttpServletResponse servletResponse) {
}
OAuth2AuthorizeRequest
would provide 2 constructors and would be similar to the members in OAuth2AuthorizationContext
which is passed downstream to the OAuth2AuthorizedClientProvider
(s).
@rwinch What are your thoughts on this proposed design?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm ok with this proposal as it solve my concern and aligns with our previous discussions (i.e. we can have two separate methods with two request types or the same method with a single request type).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jgrandja thanks for all your hard work on this! I've left some feedback inline.
This PR addresses the Servlet implementation for #6811 - the Reactive implementation will be submitted in a separate PR.
The primary responsibility of an implementation of an
OAuth2AuthorizedClientProvider
is to authorize (or re-authorize) a client. This interface would be implemented by specific grant types, e.g.authorization_code
,refresh_token
,client_credentials
,password
, custom/extension grants, etc.