The usual way to renew a token when expired is to use expiration data, coming from the token endpoint with the token response. You can cache the token for that interval and each time you need to set the bearer you first try to get it from the cache. When the token expires, the cache returns null so you request a fresh token and cache it again.
Please see the example based on the article by Dominick Baier. You will need to install the IdentityModel
nuget package if not yet done.
public class TokenClientOptions
{
public string Address { get; set; }
public string ClientId { get; set; }
public string ClientSecret { get; set; }
}
public class TokenClient
{
private const string AccessTokenCacheKey = "access_token";
public HttpClient Client { get; }
public TokenClientOptions Options { get; }
public ILogger<TokenClient> Logger { get; }
public IDistributedCache Cache { get; }
public TokenClient(HttpClient client, IOptions<TokenClientOptions> options,
IDistributedCache cache,
ILogger<TokenClient> logger)
{
Client = client;
Options = options.Value;
Cache = cache;
Logger = logger;
}
public async Task<string> GetToken()
{
var token = Cache.GetString(AccessTokenCacheKey);
if (token != null)
return token;
var response = await Client.
RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
{
Address = Options.Address,
ClientId = Options.ClientId,
ClientSecret = Options.ClientSecret
});
Cache.SetString(AccessTokenCacheKey, response.AccessToken,
new DistributedCacheEntryOptions()
{AbsoluteExpirationRelativeToNow =
TimeSpan.FromSeconds(response.ExpiresIn)});
return response.AccessToken;
}
}
public static class Extensions
{
public static void AddTokenClient(this IServiceCollection services) {
services.Configure<TokenClientOptions>(options =>
{
options.Address = "https://demo.identityserver.io/connect/token";
options.ClientId = "client";
options.ClientSecret = "secret";
});
services.AddDistributedMemoryCache();
services.AddHttpClient<TokenClient>();
}
}
then in your Startup.ConfigureServices
you add: services.AddTokenClient();
and after that you can inject TokenClient
into your API controllers and use it like you did in your sample above:
public class TestController : Controller
{
public TokenClient TokenClient { get; }
public TestController(TokenClient tokenClient) => TokenClient = tokenClient;
public async Task<HttpResponseMessage> Index()
{
var request = new HttpRequestMessage(
HttpMethod.Get, "https://demo.identityserver.io/api/test");
var accessToken = await TokenClient.GetToken();
request.SetBearerToken(accessToken);
var client = HttpClientFactory.Create();
var response = await client.SendAsync(request, new CancellationToken());
return response;
}
}