-
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
InMemory[Reactive]OAuth2AuthorizedClientService
does not support changes to the ClientRegistration
at runtime
#15511
Comments
Hi @kzander91, thanks for the report. Apologies on the delay, I'm just going through some issues that we haven't been able to get to until now.
The Typically, you would want to use |
Hi @sjohnr, no worries! I am aware that the in-memory implementations of various beans aren't recommended for production use, but my interpretation of the reason behind that was always that there's no mechanism to limit the size of the map, resulting in potential OOMEs. Are there other reasons for it not being recommended for production? In my use case, the map will only ever contain a single mapping, because the user in the |
The in-memory implementations are just not designed for production use. Any issues they have would be specific to their implementation. Having said that, we still want to fix bugs that are obviously incorrect (and not directly related to being in memory), so I think the bug you mention here could be fixed since the r2dbc version doesn't have the same issue. Would you be interested in sending a PR? |
@sjohnr sure I'll give it a shot! Would you say that the workaround I showed above was a suitable fix? Then I'll try submitting that as a PR. |
This changes `InMemoryOAuth2AuthorizedClientService.loadAuthorizedClient` (and its reactive counterpart) to always return `OAuth2AuthorizedClient` instances containing the current `ClientRegistration` as obtained from the `ClientRegistrationRepository`. Before this change, the first `ClientRegistration` instance was cached, with the effect that any changes made in the `ClientRegistrationRepository` (such as a new client secret) would not have taken effect. Closes spring-projectsgh-15511
Yes, I think so! Though I would find it preferable to use a fluent return value by nesting // @formatter:off
return (Mono<T>) this.clientRegistrationRepository.findByRegistrationId(clientRegistrationId)
.flatMap((clientRegistration) -> Mono.just(new OAuth2AuthorizedClientId(clientRegistrationId, principalName))
.mapNotNull(this.authorizedClients::get)
.map((authorizedClient) -> new OAuth2AuthorizedClient(clientRegistration,
authorizedClient.getPrincipalName(),
authorizedClient.getAccessToken(),
authorizedClient.getRefreshToken()))
);
// @formatter:on |
This changes `InMemoryOAuth2AuthorizedClientService.loadAuthorizedClient` (and its reactive counterpart) to always return `OAuth2AuthorizedClient` instances containing the current `ClientRegistration` as obtained from the `ClientRegistrationRepository`. Before this change, the first `ClientRegistration` instance was cached, with the effect that any changes made in the `ClientRegistrationRepository` (such as a new client secret) would not have taken effect. Closes spring-projectsgh-15511
This changes `InMemoryOAuth2AuthorizedClientService.loadAuthorizedClient` (and its reactive counterpart) to always return `OAuth2AuthorizedClient` instances containing the current `ClientRegistration` as obtained from the `ClientRegistrationRepository`. Before this change, the first `ClientRegistration` instance was cached, with the effect that any changes made in the `ClientRegistrationRepository` (such as a new client secret) would not have taken effect. Closes spring-projectsgh-15511
This changes `InMemoryOAuth2AuthorizedClientService.loadAuthorizedClient` (and its reactive counterpart) to always return `OAuth2AuthorizedClient` instances containing the current `ClientRegistration` as obtained from the `ClientRegistrationRepository`. Before this change, the first `ClientRegistration` instance was cached, with the effect that any changes made in the `ClientRegistrationRepository` (such as a new client secret) would not have taken effect. Closes spring-projectsgh-15511
This changes `InMemoryOAuth2AuthorizedClientService.loadAuthorizedClient` (and its reactive counterpart) to always return `OAuth2AuthorizedClient` instances containing the current `ClientRegistration` as obtained from the `ClientRegistrationRepository`. Before this change, the first `ClientRegistration` instance was cached, with the effect that any changes made in the `ClientRegistrationRepository` (such as a new client secret) would not have taken effect. Closes spring-projectsgh-15511
This changes `InMemoryOAuth2AuthorizedClientService.loadAuthorizedClient` (and its reactive counterpart) to always return `OAuth2AuthorizedClient` instances containing the current `ClientRegistration` as obtained from the `ClientRegistrationRepository`. Before this change, the first `ClientRegistration` instance was cached, with the effect that any changes made in the `ClientRegistrationRepository` (such as a new client secret) would not have taken effect. Closes spring-projectsgh-15511
This changes `InMemoryOAuth2AuthorizedClientService.loadAuthorizedClient` (and its reactive counterpart) to always return `OAuth2AuthorizedClient` instances containing the current `ClientRegistration` as obtained from the `ClientRegistrationRepository`. Before this change, the first `ClientRegistration` instance was cached, with the effect that any changes made in the `ClientRegistrationRepository` (such as a new client secret) would not have taken effect. Closes spring-projectsgh-15511
This changes `InMemoryOAuth2AuthorizedClientService.loadAuthorizedClient` (and its reactive counterpart) to always return `OAuth2AuthorizedClient` instances containing the current `ClientRegistration` as obtained from the `ClientRegistrationRepository`. Before this change, the first `ClientRegistration` instance was cached, with the effect that any changes made in the `ClientRegistrationRepository` (such as a new client secret) would not have taken effect. Closes spring-projectsgh-15511
This changes `InMemoryOAuth2AuthorizedClientService.loadAuthorizedClient` (and its reactive counterpart) to always return `OAuth2AuthorizedClient` instances containing the current `ClientRegistration` as obtained from the `ClientRegistrationRepository`. Before this change, the first `ClientRegistration` instance was cached, with the effect that any changes made in the `ClientRegistrationRepository` (such as a new client secret) would not have taken effect. Closes spring-projectsgh-15511
This changes `InMemoryOAuth2AuthorizedClientService.loadAuthorizedClient` (and its reactive counterpart) to always return `OAuth2AuthorizedClient` instances containing the current `ClientRegistration` as obtained from the `ClientRegistrationRepository`. Before this change, the first `ClientRegistration` instance was cached, with the effect that any changes made in the `ClientRegistrationRepository` (such as a new client secret) would not have taken effect. Closes spring-projectsgh-15511
Describe the bug
We're calling an API that requires JWTs for authentication. The JWTs are obtained from an authorization server using Client Credentials Grant.
The authorization server requires us to periodically rotate the client secret. We store the secret in a separate configuration service (where we can update it out-of-band) and we have implemented a custom
ReactiveClientRegistrationRepository
that retrieves the secret from there.This setup works fine until the secret is changed: We noticed that our app keeps using the previous secret, even though our
ReactiveClientRegistrationRepository
is returning the new secret.We have tracked this down to
InMemoryReactiveOAuth2AuthorizedClientService#loadAuthorizedClient
which internally uses our repository implementation:spring-security/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/InMemoryReactiveOAuth2AuthorizedClientService.java
Lines 60 to 67 in 6adc3b3
Note how the
ClientRegistration
retrieved from ourclientRegistrationRepository
(containing the current secret) is effectively ignored in favor of theClientRegistration
cached in theauthorizedClients
map (as a member ofOAuth2AuthorizedClient
). This means that the client secret that was current at the time of the first token request is used forever, even if theClientRegistration
changes afterwards.After we rotated the secret, the authorization server rejects the previous one, resulting in all token requests to fail shortly after a secret rotation.
Expected behavior
The
InMemoryReactiveOAuth2AuthorizedClientService
should consider the fact that theClientRegistration
may change at runtime and thus not ignore it after fetching it from the registration repository.Workaround
ReactiveOAuth2AuthorizedClientService
that is basically a copy ofInMemoryReactiveOAuth2AuthorizedClientService
, but with the following modification toloadAuthorizedClient
:Note that the non-reactive version has the same behaviour, so this isn't specific to the reactive implementation.
Is there a specific reason for why the service behaves that way? What would be the recommended way to implement an OAuth2 client with secrets that can change at runtime?
The R2DBC implementation for example does behave as expected and always returns the
ClientRegistration
from the repository, see here:spring-security/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/R2dbcReactiveOAuth2AuthorizedClientService.java
Lines 150 to 156 in 6adc3b3
The text was updated successfully, but these errors were encountered: