How to build a Token Provider (with cache)

If you have a service, which calls another service, chances are that you need to retrieve and store a token for authentication. In this blog post we will take a look at an approach on how to handle this, with a single class. Let’s start with the code (for those of you who just want to copy+paste) and then look at the separate parts in details afterwards.

public class TokenProvider
{
    private const string tokenKey = "my_token";
    private readonly HttpClient _client;
    private readonly IMemoryCache _cache; 

    public TokenProvider(
        HttpClient client,
        IMemoryCache cache)
    {
        _client = client;
        _cache = cache;
    }

    public async Task<string> GetToken()
    {
        TokenResponse token;
        
        if (!_cache.TryGetValue(tokenKey, out var cacheEntry))
        {
            token = await GetFreshToken();
            SaveToCache(token);        
            return token.AccessToken;
        }

        token = cacheEntry as TokenResponse;
        return token.AccessToken;
    }

    private void SaveToCache(TokenResponse token)
    {
        var options = new MemoryCacheEntryOptions();
        // We want the token to expire in cache before it 
        // actually expires, to avoid getting a token, which 
        // then expires after a few seconds.
        options.SetAbsoluteExpiration(
            TimeSpan.FromSeconds(token.ExpiresIn - 60));
        _cache.Set(tokenKey, token, options);
    }

    private async Task<TokenResponse> GetFreshToken()
    {
        var tokenRequest = new PasswordTokenRequest()
        {
            UserName = "john_doe",
            Password = "qwerty123",
            Address = "https://token-provider.com/get-token",
            ClientId = "client_id",
            ClientSecret = "client_secret"
        };

        var token = await _client
            .RequestPasswordTokenAsync(tokenRequest);
        return token;
    }
}

The first thing that is worth mentioning, is the TokenResponse type, which comes from the IdentityModel.Client namespace. This also provides for the TokenRequest type and the RequestPasswordTokenAsync extension method. You can read more about how it works in the Identity Model documentation. In short, it allows us easy access to a bunch of things, we would otherwise have to build ourselves.

It might also be worth looking at the line if (!_cache.TryGetValue(tokenKey, out var cacheEntry)). This means that if it fails to get a token from the cache, it will run the code block from the if statement.

The token request may not be exactly what you need. There are many different types of requests, all described in the documentation. Find the one that matches your need.

Finally, you need to configure the TokenProvider as a singleton in the Startup.cs file (or Program.cs, if you are using minimal api).

services.AddSingleton<TokenProvider>();

Regarding the dependencies on IHttpClient and IMemoryClient: I didn’t seem to need them configured. But if it fails, just add both of them to services as scoped.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s