Skip to content

Instantly share code, notes, and snippets.

@dealproc
Created July 5, 2015 05:13
Show Gist options
  • Save dealproc/fa762a59487d729fad7d to your computer and use it in GitHub Desktop.
Save dealproc/fa762a59487d729fad7d to your computer and use it in GitHub Desktop.
C# OAuth Token Manager
public class TokenManager {
static readonly ILog _log = LogProvider.For<TokenManager>();
string _clientId = "{your client id}";
string _secret = "{your secret}";
string _scopes = "{claims} offline_access"; // needs the offline_access to get the refresh token.
CloudConnectConfiguration _configuration;
Task _workerLoop;
CancellationTokenSource _cts;
string _accessToken;
string _refreshToken;
long _secondsBeforeTokenExpires;
DateTimeOffset _tokenExpiresAt;
public string AccessToken { get { return _accessToken; } }
public string RefreshToken { get { return _refreshToken; } }
public bool HasToken { get { return !string.IsNullOrWhiteSpace(_accessToken); } }
public TokenManager() {
Reset();
}
public async Task<bool> Initialize(CloudConnectConfiguration configuration) {
if (configuration == null) { throw new ArgumentNullException("configuration"); }
_log.Trace("Initializing Token Manager");
_configuration = configuration;
try {
var token = await GetToken();
RecordToken(token);
_cts = new CancellationTokenSource();
_workerLoop = new Task(AcquireNewToken, _cts.Token);
_workerLoop.Start();
} catch {
Reset();
return false;
}
return true;
}
public Task Stop() {
_cts.Cancel();
Reset();
return _workerLoop;
}
async void AcquireNewToken() {
while (!_cts.Token.IsCancellationRequested) {
_log.Trace("worker loop started.");
var delayBy = (_secondsBeforeTokenExpires * 0.90M) * 1000; // (when it expires * 90%) * 1000 == waiting until we are 10% of the total seconds befor expiration
// before we refresh the token, to avoid unnecessary calls to identityserver.
_log.TraceFormat("Waiting {0} seconds before refreshing token", delayBy / 1000);
try {
await Task.Delay(Convert.ToInt32(delayBy), _cts.Token);
} catch (Exception ex) {
_log.ErrorException("Task...delay failed?", ex);
}
if (_cts.Token.IsCancellationRequested) {
_log.Trace("Cancellation was requested... ending worker");
return;
}
TokenResponse token = null;
do {
try {
token = await RefreshTheToken();
} catch (Exception ex) {
_log.ErrorException("Received an error while refreshing the token...", ex);
// squelch here.
}
if (token == null) {
_log.Trace("Could not refresh the token. Waiting 10 seconds, and trying again.");
await Task.Delay(TimeSpan.FromSeconds(10));
}
} while (token == null && _tokenExpiresAt < DateTimeOffset.UtcNow);
if (token == null) {
Reset();
return;
}
RecordToken(token);
}
}
async Task<TokenResponse> GetToken() {
_log.Trace("Requesting OAuth Token");
if (string.IsNullOrWhiteSpace(_configuration.LicenseKey) || string.IsNullOrEmpty(_configuration.HardwareId)) {
_log.Info("Hardware ID or License Key are unavailable.");
return null;
}
TokenResponse token = null;
try {
// Get an Access Token.
var client = new OAuth2.OAuth2Client(BuildUri(_configuration.AuthorizationUrl), _clientId, _secret);
token = await client.RequestResourceOwnerPasswordAsync(_configuration.HardwareId, _configuration.LicenseKey, _scopes); // remember, need to provide scope(s) here.
} catch (Exception ex) {
_log.ErrorException("Trying to retrieve token from IdentityServer.", ex);
return null;
}
_log.TraceFormat("Token obtained.");
return token;
}
async Task<TokenResponse> RefreshTheToken() {
_log.Trace("Refreshing the oauth token using the provided refresh token.");
if (string.IsNullOrWhiteSpace(_refreshToken)) {
_log.Trace("Refresh Token is missing!!!");
return null;
}
TokenResponse token = null;
try {
var client = new OAuth2Client(BuildUri(_configuration.AuthorizationUrl), _clientId, _secret);
token = await client.RequestRefreshTokenAsync(_refreshToken);
} catch (Exception ex) {
_log.ErrorException("Trying to slide the refresh token expiration failed.", ex);
}
_log.Trace("Token has been refreshed");
return token;
}
Uri BuildUri(string baseUrl) {
var url = baseUrl;
if (url.EndsWith("/")) {
url = url.Substring(0, url.Length - 1);
}
url += "/connect/token";
_log.TraceFormat("built uri will be: {0}", url);
return new Uri(url);
}
private void Reset() {
_log.Trace("Resetting token manager to default settings.");
if (_cts != null) {
_cts.Dispose();
_cts = null;
}
if (_workerLoop != null) {
_workerLoop = null;
}
_accessToken = string.Empty;
_refreshToken = string.Empty;
_secondsBeforeTokenExpires = -1;
_tokenExpiresAt = DateTimeOffset.MinValue;
_configuration = null;
}
private void RecordToken(TokenResponse token) {
_log.Trace("Recording token values.");
_accessToken = token.AccessToken;
_refreshToken = token.RefreshToken;
_secondsBeforeTokenExpires = token.ExpiresIn;
_tokenExpiresAt = DateTimeOffset.UtcNow.AddSeconds(_secondsBeforeTokenExpires);
_log.TraceFormat("Has Access Token: {0}", !string.IsNullOrWhiteSpace(_accessToken));
_log.TraceFormat("Has Refresh Token: {0}", !string.IsNullOrWhiteSpace(_refreshToken));
_log.TraceFormat("Token expires in {0}s", _secondsBeforeTokenExpires);
_log.TraceFormat("Token expires at {0}", _tokenExpiresAt);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment