Created
July 5, 2015 05:13
-
-
Save dealproc/fa762a59487d729fad7d to your computer and use it in GitHub Desktop.
C# OAuth Token Manager
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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