paulpitchford
paulpitchford

Reputation: 560

Blazor and OAuth JWT Bearer Token Storage

We're creating a small Blazor app which accesses a 3rd party api which we have no control over. The API is secured using OAuth with a client_id and client_secret on a machine to machine basis. There is no authentication from the user.

Initially we hit the token endpoint with the id and secret and it returns a token with an expiry date and time. Currently we just repeat the initial token request before every call to the api so that the token is always brand new and has never expired.

Is this practice accepted or should I be storing the bearer token and it's expiration date and time somewhere secure on the client to check the expiration before requesting a new token?

I'm conscious that if the app is widely used internally at our business we could start to reach the api usage limits, but also, if we store it locally on the client, could I mistakenly leave us vulnerable to XSS attacks or similar.

I'm looking for best practice guidance really, and, if the advice is to store the token and check against expiry, where and how should I be storing the token securely?

Upvotes: 3

Views: 2037

Answers (1)

paulpitchford
paulpitchford

Reputation: 560

I found a nice solution at:

https://referbruv.com/blog/using-imemorycache-for-token-caching-in-an-aspnet-core-application/

The author gives an excellent demonstration of how to use IMemoryCache for my exact scenario. I implement a Token Service which checks the expiry of the cache item. If it's in date, I just used the cached token. If the token has expired, I fetch a new token from the API.

public class TokenService : ITokenService
{
    private readonly IApiAccountService _apiAccountService;
    private readonly IMemoryCache _cache;

    public TokenService(IMemoryCache cache, IApiAccountService apiAccountService)
    {
        _cache = cache;
        _apiAccountService = apiAccountService;
    }

    public async Task<string> FetchTokenAsync()
    {
        string token;
        if (!_cache.TryGetValue("Token", out token))
        {
            var authResult = await _apiAccountService.GetToken();
            var options = new MemoryCacheEntryOptions().SetAbsoluteExpiration(TimeSpan.FromSeconds((authResult.expires_at - DateTime.Now).TotalSeconds));
            _cache.Set("Token", authResult.access_token, options);
            token = authResult.access_token;
        }

        return token;
    }
}

Then, when calling my API I just call the Token Service:

private readonly ITokenService _tokenService;

    public ApiService(HttpClient httpClient, IConfiguration configuration, ITokenService tokenService)
    {
        _httpClient = httpClient;
        _configuration = configuration;
        _tokenService = tokenService;
    }

...

    private async Task<List<T>> FetchListAsync<T>(string uri)
    {
        _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", await _tokenService.FetchTokenAsync());
        var response = await _httpClient.GetAsync(uri);
        
        response.EnsureSuccessStatusCode();
        
        using var responseStream = await 
        response.Content.ReadAsStreamAsync();
        var reader = new StreamReader(responseStream).ReadToEnd();
        var responseObject = JsonConvert.DeserializeObject<List<T>>(reader);
        
        return responseObject;
    }

Upvotes: 2

Related Questions