How to Token Caching with IMemoryCache in ASP.NET Core

In this article, let's see how we can use IMemoryCache to cache JWT Tokens in an ASP.NET Core application

Recently I was working on a requirement in ASP.NET Core which needed to invoke a third-party API for fetching some JSON content.

The implementation was pretty simple – I had to invoke an external API for every input request and return the records returned by the API; pretty much the role of an aggregator for some underlying data services.

But this implementation had a catch – the underlying API which needed to be invoked had a Token Authentication in place. Hence for every request made to the underlying API I had to pass a valid non-expired Access_Token to the API, in order to fetch the data.

Furthermore, this token expires after a certain period which needed to be fetched from a Token endpoint.

Now the real question arises – How do I persist this fetched token until its expiry without having to call the OAuth API each time a request for data is made?

In such scenarios, where persisting less frequently changed data such as tokens (which may be unchanged for few minutes for example) we can cache the token somewhere and ensure that its reused whenever required until its expiry. This way, I don’t need to ask for a Token from the Token Endpoint every time and one less API call means a little better performance.

Where do I cache this token? I can use the InMemory Caching feature that comes with ASP.NET Core.

How does In-Memory Caching work?

The In-Memory cache provided within the application can hold small chunks of data for predefined duration so as to reuse the already existing dataset for repetitive calls.

Since it is stored in the memory of the running application (unlike external cache managers such as Redis and such) we should be careful about the size of the data we’re storing in the cache.

Read: Configuring Response Cache in an ASP.NET Core API

Coming back to my case, Creating a Caching Token Service

Here’s how I designed my solution. I create a utility class TokenService which implements an interface ITokenService containing a single method FetchToken that returns a string value.

Since I use this only to store and fetch tokens, I don’t need any variation of this functionality. If in case I want to store multiple tokens from different sources, I can pass a cachingKey parameter to this FetchToken() method which can return me the token i’m looking for.

namespace ReadersApi
{
    public interface ITokenService
    {
        string FetchToken();
    }
}

Now this is implemented as below:

namespace ReadersApi
{
    public class TokenService : ITokenService
    {
        private readonly IMemoryCache cache;

        public TokenService(IMemoryCache cache)
        {
            this.cache = cache;
        }

        public string FetchToken()
        {
            string token = string.Empty;

            // if cache doesn't contain 
            // an entry called TOKEN
            // error handling mechanism is mandatory
            if (!cache.TryGetValue("TOKEN", out token))
            {
                var tokenmodel = this.GetTokenFromApi();

                // keep the value within cache for 
                // given amount of time
                // if value is not accessed within the expiry time
                // delete the entry from the cache
                var options = new MemoryCacheEntryOptions()
                        .SetAbsoluteExpiration(
                              TimeSpan.FromSeconds(tokenmodel.ExpiresIn));

                cache.Set("TOKEN", tokenmodel.Value, options);

                token = tokenmodel.Value;
            }

            return token;
        }

        private Token GetTokenFromApi()
        {
            // get api implementation happens here
            // returns a token model
        }
    }
}

Where the Token is a model class as below:

class Token
{
    public string Value { get; set; }
    public int ExpiresIn { get; set; }
}

I then register this TokenService as a singleton service, along with registering memory cache as a service to the pipeline.

namespace ReadersApi
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMemoryCache();
            services.AddSingleton<ITokenService, TokenService>();
        }
    }
}

Adding MemoryCache is not necessary required because it is enabled by default for all controllers and other services which are added by Add<>() method.

However, for other classes or components which may not be calling the Add<>() method, AddMemoryCache() might be required. Hence I make it a practice to add it explicitly.

When I’m required to use the token for my API request within my application, I simply inject the TokenService via constructor and call the FetchToken() method, which checks for any available cache that has not expired.

If available, will return the cache value directly; else would fallback on the token retrieval logic we have employed in case if the token expires. The received token is then set to a new cache object and stored in-memory and the token is returned back to the caller.

I can call this method wherever I want the Token to be substituted. For example, I can use this inside my API call code where I need to pass a Token Header. I’ve implemented it as below:

public class HttpService
{
    private ITokenService token;

    public HttpService(ITokenService token)
    {
        this.token = token;
    }

    public async Task<IEnumerable<Reader>> GetReadersFromExternalApi(string requestUrl)
    {
        using (HttpClient client = new HttpClient())
        {
            var request = new HttpRequestMessage(HttpMethod.Get, requestUrl);
            request.Headers.Add("Authorization",
 
quot;Bearer {token.FetchToken()}"); var res = await client.SendAsync(request); var readers = JsonConvert.DeserializeObject<List<Reader>>( await res.Content.ReadAsStringAsync()); return readers; } } } 

Sliding vs Absolute Expiry in Cache

While creating an In-Memory cache object, we specify how the cache expiry needs to be handled.

When we set a SlidingExpiry to the cache object, the object expiry is extended if the cache is accessed at least once within the provided cache expiry time.

But this comes with a catch – if the cached object is accessed occasionally or frequently, chances are that the cache never expires (since its always extended) which might be a problem for certain scenarios (such as the one above).

The Absolute Expiry on the other hand expires a cache absolutely when the cache expires irrespective of how it is being accessed. This can be quite helpful in cases when the cache needs to be strictly expired when time ends.

In some scenarios, we might need the best of both worlds and hence we can specify both together as below:

var cacheEntry = _cache.GetOrCreate("TOKEN", entry =>
{
    // set a sliding initial expiry of 1 minute
    // assuming that the token expiry is above 1 minute
    entry.SetSlidingExpiration(TimeSpan.FromSeconds(60));
        
    // set absolute expiry relative to current time
    // makes sure that the extended expiry doesn't exceed 
    // the actual expiry time of the token
    entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(tokenmodel.ValidTill);
    return DateTime.Now;
});

In the above code, I’ve configured my Cache to strictly expire when the Token expires (I receive it from my Token endpoint) while a shorter SlidingExpiry than the AbsoluteExpiry means the token is still persisted until its absolute expiry whenever its accessed.

Buy Me A Coffee

Found this article helpful? Please consider supporting!

In this way, I used in-memory caching via IMemoryCache to help persist my intermediate access token for calling external APIs within my application, without having to call the token API repeatedly.

Sriram Mannava
Sriram Mannava

I'm a full-stack developer and a software enthusiast who likes to play around with cloud and tech stack out of curiosity.

One comment

Leave a Reply

Your email address will not be published.