[OAuth2] Enable configurable early token refresh in Java Client#13951
[OAuth2] Enable configurable early token refresh in Java Client#13951michaeljmarshall wants to merge 7 commits intoapache:masterfrom
Conversation
54db563 to
012d641
Compare
eolivelli
left a comment
There was a problem hiding this comment.
Isn't creating a new thread per each client instance too heavyweight?
Is there a way to grab the timer from the client?
There are systems that need to create multiple clients (like the Pulsar proxy did)
pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/AuthenticationOAuth2.java
Outdated
Show resolved
Hide resolved
pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/AuthenticationOAuth2.java
Outdated
Show resolved
Hide resolved
lhotari
left a comment
There was a problem hiding this comment.
The lifecycle of the scheduler/executor will need to be managed. This solution would lead to resource leaks. I looked into ways how to possible solve this problem, but it's not easy. The main reason seems to be that the current API for OAuth2 in the Pulsar client is not great. I didn't find a way to extend it without breaking backwards compatibility. I think it's necessary to redesign the Pulsar client's API for configuring OAuth2 and take these requirements into account. It's more or less a blocker for making progress.
I think that the way to specify the refresh interval by using the "early_token_refresh_percent" configuration parameter is not well suited for all use cases. Instead of that, it might be better to have ways to express boundaries using 3 different parameters. There are 2 alternative ways to express this: one approach would be to consider it to be a "soft expiration" where the expiration is limited by the parameters, another approach is to think of it as a "refresh interval".
Let's say that the thinking is around "refresh interval", there could be 3 parameters
- "refresh before expiration duration"
- for example, with the value 600 seconds, will start refreshing the token 10 minutes before it expires
In addition, these parameters could be used to set the boundaries for the value calculated based on the "refresh before expiration duration" parameter:
- for example, with the value 600 seconds, will start refreshing the token 10 minutes before it expires
- "minimum refresh interval"
- "maximum refresh interval"
|
@lhotari - I agree that we need to take care of the life cycle of the thread used for these refresh token commands. In my most recent commit, I changed the behavior so that the application can either supply an executor, or the OAuth2 class will create one (assuming that the feature is enabled, which it is not by default). The one downside here is that this means the feature is not available for brokers, because they create the
Would you mind clarifying this use case with an example? On one hand, I accept that percent may be slightly less straight forward, but on the other hand, I think it handles certain edge cases really well. For example, each retrieved token can have a unique |
| * Set the {@link ClientCredentialsConfiguration} when using the OAuth2 client credentials flow. | ||
| * @return builder | ||
| */ | ||
| public AuthenticationOAuth2Builder setClientCredentialsConfiguration( |
There was a problem hiding this comment.
In Pulsar, builders usually don't use set* naming convention. it would be good to follow a similar naming convention that has been used in other parts of Pulsar. for example, rename setClientCredentialsConfiguration -> clientCredentialsConfiguration.
| * {@link AuthenticationOAuth2} for details. | ||
| * @return builder | ||
| */ | ||
| public AuthenticationOAuth2Builder setEarlyTokenRefreshPercent(double earlyTokenRefreshPercent) { |
| * {@link AuthenticationOAuth2} will not close it. | ||
| * @return builder | ||
| */ | ||
| public AuthenticationOAuth2Builder setEarlyTokenRefreshExecutor(ScheduledThreadPoolExecutor scheduler) { |
|
|
||
| public Authentication build() { | ||
| if (clientCredentialsConfiguration == null) { | ||
| throw new IllegalArgumentException("ClientCredentialsConfiguration must be set."); |
|
|
||
| private final String audience; | ||
| private final String privateKey; | ||
| private final String keyFileUrl; |
There was a problem hiding this comment.
keyFileUrl seems repetitive and conflicting. Is it a File or an URL? What key is it? Perhaps keeping the name privateKey and switching to use types would improve clarity. I'd recommend java.net.URI class also for URLs. that would make this URI privateKey.
|
|
||
| public static final String CONFIG_PARAM_TYPE = "type"; | ||
| public static final String TYPE_CLIENT_CREDENTIALS = "client_credentials"; | ||
| public static final int EARLY_TOKEN_REFRESH_PERCENT_DEFAULT = 1; // feature disabled by default |
There was a problem hiding this comment.
I'll reply separately to the earlyTokenRefreshPercent question
The use case would be that you want to maximize the time that a token is active at all times. The reason to do this is that if the identity provider becomes unavailable, there is less risk the whole system becomes unavailable because of the identity provider being unavailable. The system will tolerate a long downtime when the tokens are always active for the maximum period of time. The tradeoff is that there will be more token refreshes made towards the identity provider. You might want to refresh the token every 10 minutes regardless of the validity of the token, given that the token is valid for more than 10 minutes. The solution I proposed wasn't probably the best way to address this use case. Another person might want to refresh the token 10 minutes before the validity of the token expires, regardless of how long the token is valid. For the above usecases, I would assume that configuring the solution would be possible with 2 parameters (naming is hard, so the names are still bad): The problem with the 10 minutes before token expires example is the case where the token is valid for less than 10 minutes. That's why it is necessary to have a second parameter to define how long the token is kept used until a refresh is made. Let's say that the user has set "refresh before expiration duration" to 10 minutes and "minimum token usage duration" to 2 minutes. If the returned token is valid for 10 minutes, it would start the refresh after 2 minutes. I simply don't see a use case for the earlyTokenRefreshPercent way of configuring the refresh scheduling. In the requirement we have, it's about exact times instead of percentages. I think that percentages would be confusing for users. "refresh before expiration duration" |
|
@lhotari - thanks for clarifying your point. I hadn't thought of the use case where a user has a refresh period that is independent of the token's time to live. Of the two parameters, which should take precedence? I think we should use the minimum duration resulting from either |
|
The pr had no activity for 30 days, mark with Stale label. |
|
The pr had no activity for 30 days, mark with Stale label. |
|
@michaeljmarshall This PR is likely beyond stale. Would you please close. |
|
I took over completing this PR. However, I couldn't push changes to the PR branch so I opened a new PR #25363 |
Motivation
In some client use cases, it is helpful to start attempting to refresh the OAuth2 token before it has expired. This PR adds that functionality.
The core addition is the ability to refresh the OAuth2 token in the background without affecting the current token. This will improve stability for OAuth2 users, especially if/when their IDP is unavailable.
Modifications
ScheduledThreadPoolExecutorto theAuthenticationOAuth2and schedule refresh token tasks to refresh a token before it has expired.Verifying this change
This PR includes new tests. There are also existing tests that cover some of the changes.
Does this pull request potentially affect one of the following parts:
This PR changes the default behavior of the
AuthenticationOAuth2class and it also adds a new configuration value for end users. By default, it does not change the current behavior.Documentation
docI added docs.