I am consuming an asynchronous Web API that requires an AccessToken
(an immutable struct) to be passed as an argument on every API call. This AccessToken
is itself obtained by calling an asynchronous Authenticate
method of the same Web API.
class WebApi
{
public Task<AccessToken> Authenticate(string username, string password);
public Task PurchaseItem(AccessToken token, int itemId, int quantity);
// More methods having an AccessToken parameter
}
I don't want to call the Authenticate
method before calling every other method of the API, for performance reasons. I want to call it once, and then reuse the same AccessToken
for multiple API calls. My problem is that the AccessToken
is expiring every 15 minutes, and calling any API method with an expired AccessToken
results to an AccessTokenExpiredException
. I could catch this exception and then retry the faulted call, after acquiring a new AccessToken
, but I would prefer to preemptively refresh the AccessToken
before it has expired, again for performance reasons. My application is multithreaded, so multiple threads might try to use/refresh the same AccessToken
value concurrently, and things quickly start to become very messy.
The requirements are:
- The
Authenticate
method should not be called more frequently than once every 15 minutes, even if multiple threads attempt to invoke methods of the Web API concurrently. - In case an
Authenticate
call fails, it should be repeated the next time anAccessToken
is needed. This requirement takes precedence over the previous requirement. Caching and reusing a faultedTask<AccessToken>
for 15 minutes is not acceptable. - The
Authenticate
method should be called only when anAccessToken
is actually needed. Invoking it every 15 minutes with aTimer
is not acceptable. - An
AccessToken
should only be used during the next 15 minutes after its creation. - The expiration mechanism should not be dependent on the system clock. A system-wise clock adjustment should not affect (elongate or shorten) the expiration period.
My question is: how could
I abstract the functionality of acquiring, monitoring the expiration, and refreshing the AccessToken
, in a way that satisfies the requirements, while keeping the rest of my application clean from all this complexity? I am thinking of something similar to the AsyncLazy<T>
type that I found in this question:
Enforce an async method to be called once, but enhanced with expiration functionality. Here is a hypothetical example of using this type (enhanced with a TimeSpan
parameter):
private readonly WebApi _webApi = new WebApi();
private readonly AsyncLazy<AccessToken> _accessToken = new AsyncLazy<AccessToken>(
() => _webApi.Authenticate("xxx", "yyy"), TimeSpan.FromMinutes(15));
async Task Purchase(int itemId, int quantity)
{
await _webApi.PurchaseItem(await _accessToken, itemId, quantity);
}
Btw this question was inspired by a recent question, where the OP was trying to solve a similar problem in a different way.