Add support for additional dynamic OIDC Token Exchange Request parameters
Expected Behavior
In a project I am currently working on, we must send additional request parameters in the OIDC Token Exchange grant request. I want a simpler way to add and define additional parameters to be sent with the token exchange grant request.
Current Behavior
The current implementation of https://github.com/spring-projects/spring-security/blob/0e3cfd1efbd0aba7e9890f695a622db2db70ddf9/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/TokenExchangeOAuth2AuthorizedClientProvider.java#L44 does not allow customization to this level. What is possible, is adding a static list of additional parameters to the grant request by passing a customized contextAttributesMapper to the https://github.com/spring-projects/spring-security/blob/0e3cfd1efbd0aba7e9890f695a622db2db70ddf9/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/DefaultOAuth2AuthorizedClientManager.java#L88.
Context
To be more specific, when calling OAuth2AuthorizedClientManager::authorize, which will perform a token exchange for this client registration, the request must in my case include an additional parameter to be sent.
This is not possible without completely replacing the implementation of TokenExchangeOAuth2AuthorizedClientProvider for the following reasons:
- The
parametersConverterof theRestClientTokenExchangeTokenResponseClientonly has access to theTokenExchangeGrantRequestas an input that might contain per-exchange dynamic parameters. - None of the attributes set in the
OAuth2AuthorizeRequestpassed toOAuth2AuthorizedClientManager::authorizeend up in theTokenExchangeGrantRequestobject. - And even if that were the case, a second call to authorize the client with a different dynamic parameter would not result in another token exchange, as the context thinks the client is already authorized because of this snippet in
TokenExchangeOAuth2AuthorizedClientProvider: https://github.com/spring-projects/spring-security/blob/0e3cfd1efbd0aba7e9890f695a622db2db70ddf9/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/TokenExchangeOAuth2AuthorizedClientProvider.java#L76-L81
I was able to circumvent these short-comings by:
- Extending
OAuth2AuthorizedClientby adding an additional field for the additional attributes. - Copying
TokenExchangeOAuth2AuthorizedClientProviderand extending the check for re-authorization using the new field fromOAuth2AuthorizedClient. - Using a
CustomTokenExchangeGrantRequestthat extendsTokenExchangeGrantRequestand includes my dynamic parameters for the request. - Using a
parametersConverterthat casts theTokenExchangeGrantRequestto aCustomTokenExchangeGrantRequestand sets the additional parameters` - Adding the additional attributes to the
OAuth2AuthorizationContext.attributes()to compare them to the last request in the nextauthorize(...)call. - Extending the check for re-authorizization and non-expired token by comparing the additional parameters with the last ones.
Yes, this explanation is probably not easy to follow, so I am willing to create a PR that would support my use case in a slightly generalized way for every one to use.
I expect the change to be simpler than what I described above, as my complications mainly arose from the fact that TokenExchangeOAuth2AuthorizedClientProvider is a final class.
Upon implementing a PR to see if my proposed feature can be implemented without too many changes, I realized that my current workaround does not even work as I thought it did.
The custom extended OAuth2AuthorizedClient stored in the context does not retain the additional new fields. I incorrectly assumed that this object would be stored as-is, but it is recreated at a later stage in https://github.com/spring-projects/spring-security/blob/72a2831f765cd6d835086132d0e893ad129aa2f0/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/InMemoryOAuth2AuthorizedClientService.java#L88. My suggested PR recreates the OAuth2AuthorizedClient at this stage with the additional attributes from the authorized client.