Skip to content

Instantly share code, notes, and snippets.

@anderbakk
Created July 19, 2017 15:31
Show Gist options
  • Select an option

  • Save anderbakk/03b02bb764cdd94908d93424de16124b to your computer and use it in GitHub Desktop.

Select an option

Save anderbakk/03b02bb764cdd94908d93424de16124b to your computer and use it in GitHub Desktop.
Caching TokenResponse from IdentityModel.Client.TokenClient
using System;
using System.Diagnostics;
using IdentityModel.Client;
namespace IfInsurance.Waypoint.Cached.Identity.TokenClient
{
public interface ICachedTokenClient
{
string GetToken(bool forceFreshToken = false);
}
public class CachedTokenClient : IClient
{
private readonly object _lockObject = new object();
private TokenResponse _cachedTokenResponse;
private DateTime _timeToRefreshToken;
private readonly string _authority;
private readonly string _clientId;
private readonly string _clientSecret;
private readonly string _api;
public CachedTokenClient(string authority, string clientId, string clientSecret, string api)
{
_authority = string.IsNullOrEmpty(authority) ? throw new ArgumentNullException(nameof(authority)) : authority;
_clientId = string.IsNullOrEmpty(clientId) ? throw new ArgumentNullException(nameof(clientId)) : clientId;
_clientSecret = string.IsNullOrEmpty(clientSecret) ? throw new ArgumentNullException(nameof(clientSecret)) : clientSecret;
_api = string.IsNullOrEmpty(api) ? throw new ArgumentNullException(nameof(api)) : api;
}
public string GetToken(bool forceFreshToken = false)
{
lock(_lockObject)
{
if (forceFreshToken || HasValidToken() == false)
{
RefreshToken();
}
return _cachedTokenResponse.AccessToken;
}
}
private void RefreshToken()
{
var disco = GetDiscoveryResponse();
var tokenClient = new IdentityModel.Client.TokenClient(disco.TokenEndpoint, _clientId,
_clientSecret);
var tokenResponse = tokenClient.RequestClientCredentialsAsync(_api)
.ConfigureAwait(false).GetAwaiter().GetResult();
if (tokenResponse == null || tokenResponse.IsError)
throw new ArgumentException(tokenResponse?.ErrorDescription ?? "Error requesting api access");
_timeToRefreshToken = DateTime.Now.AddSeconds(tokenResponse.ExpiresIn);
_cachedTokenResponse = tokenResponse;
Trace.TraceInformation($"Token retrieved, expires in {tokenResponse.ExpiresIn} seconds");
}
private bool HasValidToken()
{
return _cachedTokenResponse != null && DateTime.Now.AddMinutes(-10) < _timeToRefreshToken;
}
private DiscoveryResponse GetDiscoveryResponse()
{
var disco = DiscoveryClient.GetAsync(_authority).GetAwaiter().GetResult();
if (disco == null || disco.IsError || string.IsNullOrEmpty(disco.TokenEndpoint))
throw new Exception($"Could not get TokenEndpoint from DiscoveryClient");
return disco;
}
}
}
@GFoley83
Copy link

I think there's an error in your HasValidToken() logic.

It looks as though you want to refresh the token 10 minutes before it expires. In that case, DateTime.UtcNow.AddMinutes(-10) should be DateTime.UtcNow.AddMinutes(10).

If _timeToRefreshToken was say, set to 11am and HasValidToken() ran at 11.06 am. 11.06am - 10 minutes is 10.56am, which means your logic says the token is still valid, even though it expired at 11am. So what you're after is:

return _cachedTokenResponse != null && DateTime.Now.AddMinutes(10) < _timeToRefreshToken;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment