I found this answer to be woefully incomplete:
Refresh Token using Polly with Typed Client
Same question as in this post, except the accepted answer seems critically flawed. Every time you make a request, you're going to get a 401 error, then you obtain an access token, attach it to the request and try again. It works, but you take an error on every single message.
The only solution I see is to set the default authentication header, but to do that, you need the HttpClient
. So the original answer would need to do something like this:
services.AddHttpClient<TypedClient>()
.AddPolicyHandler((provider, request) =>
{
return Policy.HandleResult<HttpResponseMessage>(r => r.StatusCode == HttpStatusCode.Unauthorized)
.RetryAsync(1, (response, retryCount, context) =>
{
var httpClient = provider.GetRequiredService<HttpClient>();
var authService = provider.GetRequiredService<AuthService>();
httpClient.DefaultRequestHeaders.Authorization = authService.GetAccessToken());
});
});
});
So the rub is, how do you get the current HttpClient
in order to set the default access token so you don't call this handler every time you make a request? We only want to invoke this handler when the token has expired.
As I have stated in the comments I've already created two solutions:
Both of the solutions are utilizing named clients. So, here I would focus only on that part which needs to be changed to use typed client.
The good news is that you need to change only a tiny part of the solutions.
Only the following code needs to be changed from this:
services.AddHttpClient("TestClient")
.AddPolicyHandler((provider, _) => GetTokenRefresher(provider))
.AddHttpMessageHandler<TokenFreshnessHandler>();
to this:
services.AddHttpClient<ITestClient, TestClient>
.AddPolicyHandler((provider, _) => GetTokenRefresher(provider))
.AddHttpMessageHandler<TokenFreshnessHandler>();
The ITestClient
and TestClient
entities are independent from the rest of the solution.
Only the following code needs to be changed
services.AddHttpClient("TestClient")
.AddPolicyHandler((sp, request) => GetTokenRefresher(sp, request))
.AddHttpMessageHandler<TokenRetrievalHandler>()
to this:
services.AddHttpClient<ITestClient, TestClient>
.AddPolicyHandler((sp, request) => GetTokenRefresher(sp, request))
.AddHttpMessageHandler<TokenRetrievalHandler>()
So the rub is, how do you get the current
HttpClient
in order to set the default access token so you don't call this handler every time you make a request? We only want to invoke this handler when the token has expired.
With my proposed solution you don't need to access the HttpClient
to set the default header, because the currently active access token is stored in a singleton class (TokenService
). Inside the DelegatingHandler
(TokenFreshnessHandler
/TokenRetrievalHandler
) you retrieve the latest, greatest token and set it on the HttpRequestMessage
.