This topic contains the following sections.
TokenManager.cs
TokenManager.cs | ![]() |
---|---|
using System; using System.Runtime.Caching; namespace FaxWSConsoleApplication { /// <summary> /// Helper class /// Implements a Cache and RestApiClient to store and request Access tokens /// </summary> public class TokenManager { private readonly MemoryCache cache = MemoryCache.Default; private readonly string key = "tokenresponse_"; private readonly RestApiClient authClient; public TokenManager(string endpoint) { authClient = new RestApiClient(endpoint); } /// <summary> /// Retrieve cached AccessTokenResponse object for a user /// </summary> /// <param name="username"></param> /// <returns></returns> public AccessTokenResponse GetCachedAccessTokenResponse(string username) { string cacheKey = key + username; return cache.Get(cacheKey) as AccessTokenResponse; } /// <summary> /// Store or update AccessTokenResponse in cache for a user /// </summary> /// <param name="username"></param> /// <param name="tokenResponse"></param> public void AddOrUpdateCachedAccessTokenResponse(string username, AccessTokenResponse tokenResponse) { string cacheKey = key + username; if(tokenResponse == null) { return; } // TODO: Cache can expire and cause the loss of the refresh token, which will require the need for a new access token cache.Set(cacheKey, tokenResponse, tokenResponse.ValidTo.Value.AddMinutes(15)); } /// <summary> /// Remove cached AccessTokenResponse for user /// </summary> /// <param name="username"></param> /// <returns></returns> public AccessTokenResponse RemoveCachedAccessTokenResponse(string username) { string cacheKey = key + username; return cache.Remove(cacheKey) as AccessTokenResponse; } /// <summary> /// Request an AccessTokenResponse from Cache for a user, or if not found request from the Identity server /// </summary> /// <param name="request"></param> /// <param name="ignoreCache"></param> /// <returns></returns> public AccessTokenResponse GetAccessToken(AccessTokenRequest request, bool ignoreCache = false) { AccessTokenResponse accessTokenResponse = null; AccessTokenRequest accessTokenRequest = null; if(!ignoreCache) { // Get Existing OAuth Access Token from Cache accessTokenResponse = GetCachedAccessTokenResponse(request.Username); } // If we have a valid token in cache attempt to use it if(accessTokenResponse != null && !accessTokenResponse.IsExpired) { Console.WriteLine("AccessToken found in Cache for user: {0}", request.Username); return accessTokenResponse; } // Token not found in Cache if(accessTokenResponse == null) { Console.WriteLine("Calling GetAccessToken for user: {0}", request.Username); // Create OAuth Token Request accessTokenRequest = new AccessTokenRequest( clientId: request.ClientId, clientSecret: request.ClientSecret, username: request.Username, password: request.Password, grantType: request.GrantType, scope: request.Scope, redirectUri: request.RedirectUri, code: request.Code, refreshToken: request.RefreshToken, state: request.State); // Get New OAuth Access Token accessTokenResponse = authClient.GetToken(accessTokenRequest); if(accessTokenResponse.HttpStatusCode != System.Net.HttpStatusCode.OK) { Console.WriteLine("GetAccessToken Call Failed"); Console.WriteLine("HttpStatusCode: {0}, Error Code: {1}, Error Description: {2}", accessTokenResponse.HttpStatusCode, accessTokenResponse.Error, accessTokenResponse.ErrorDescription); return accessTokenResponse; } // Store OAuth Access Token Response to Cache for re-use Console.WriteLine("Storing AccessTokenResponse to Cache"); AddOrUpdateCachedAccessTokenResponse(request.Username, accessTokenResponse); return accessTokenResponse; } // Token is expired and we don't have a refresh token if(string.IsNullOrEmpty(accessTokenResponse.RefreshToken)) { Console.WriteLine("Token expired, calling GetAccessToken for user: {0}", request.Username); // Create OAuth Token Request accessTokenRequest = new AccessTokenRequest( clientId: request.ClientId, clientSecret: request.ClientSecret, username: request.Username, password: request.Password, grantType: request.GrantType, scope: request.Scope, redirectUri: request.RedirectUri, code: request.Code, refreshToken: request.RefreshToken, state: request.State); // Get New OAuth Access Token accessTokenResponse = authClient.GetToken(accessTokenRequest); if(accessTokenResponse.HttpStatusCode != System.Net.HttpStatusCode.OK) { Console.WriteLine("GetAccessToken Call Failed"); Console.WriteLine("HttpStatusCode: {0}, Error Code: {1}, Error Description: {2}", accessTokenResponse.HttpStatusCode, accessTokenResponse.Error, accessTokenResponse.ErrorDescription); return accessTokenResponse; } // Store OAuth Access Token Response to Cache for re-use Console.WriteLine("Storing AccessTokenResponse to Cache"); AddOrUpdateCachedAccessTokenResponse(request.Username, accessTokenResponse); return accessTokenResponse; } // Token is expired and has the ability to use a refresh token if(!string.IsNullOrEmpty(accessTokenResponse.RefreshToken)) { Console.WriteLine("Token expired, calling GetRefreshToken for user: {0}, with token: {1}...", request.Username, accessTokenResponse.RefreshToken?.Substring(0, 8)); // Create OAuth Token Refresh Request accessTokenRequest = new AccessTokenRequest( clientId: request.ClientId, clientSecret: request.ClientSecret, username: null, password: null, grantType: "refresh_token", scope: request.Scope, redirectUri: null, code: null, refreshToken: accessTokenResponse.RefreshToken, state: null); // Get New OAuth Access token using Refresh token accessTokenResponse = authClient.GetToken(accessTokenRequest); if(accessTokenResponse.HttpStatusCode != System.Net.HttpStatusCode.OK) { Console.WriteLine("GetRefreshToken Call Failed"); Console.WriteLine("HttpStatusCode: {0}, Error Code: {1}, Error Description: {2}", accessTokenResponse.HttpStatusCode, accessTokenResponse.Error, accessTokenResponse.ErrorDescription); return accessTokenResponse; } // Store OAuth Access Token Response to Cache for re-use Console.WriteLine("Storing AccessTokenResponse to Cache"); AddOrUpdateCachedAccessTokenResponse(request.Username, accessTokenResponse); return accessTokenResponse; } Console.WriteLine("Oops, we should not have gotten here"); return null; } } } |
RestApiClient.cs
RestApiClient.cs | ![]() |
---|---|
using System; using System.Collections.Generic; using System.Net; using System.Net.Http; using System.Text.Json; namespace FaxWSConsoleApplication { /// <summary> /// Helper class /// Talks with the Identity server /// </summary> public class RestApiClient : IDisposable { #region Variables protected readonly string _oauthUrl; protected HttpClient _client = null; private bool disposed; #endregion public RestApiClient(string oauthUrl) { _client = new HttpClient(); _oauthUrl = oauthUrl; } public AccessTokenResponse GetToken(AccessTokenRequest request) { AccessTokenResponse response = new AccessTokenResponse(); var postResponse = PostData(_oauthUrl, request); try { response = JsonSerializer.Deserialize<AccessTokenResponse>(postResponse.Content); response.HttpStatusCode = postResponse.StatusCode; } catch(Exception ex) { response.ErrorDescription = ex.Message; response.Error = postResponse.Content; response.HttpStatusCode = HttpStatusCode.InternalServerError; } return response; } private PostResponse PostData(string url, AccessTokenRequest request) { var postResponse = new PostResponse(); var data = new List<KeyValuePair<string, string>>(); // Convert request to key value pair if(!string.IsNullOrEmpty(request.ClientId)) data.Add(new KeyValuePair<string, string>("client_id", request.ClientId)); if(!string.IsNullOrEmpty(request.ClientSecret)) data.Add(new KeyValuePair<string, string>("client_secret", request.ClientSecret)); if(!string.IsNullOrEmpty(request.Username)) data.Add(new KeyValuePair<string, string>("username", request.Username)); if(!string.IsNullOrEmpty(request.Password)) data.Add(new KeyValuePair<string, string>("password", request.Password)); if(!string.IsNullOrEmpty(request.Scope)) data.Add(new KeyValuePair<string, string>("scope", request.Scope)); if(!string.IsNullOrEmpty(request.GrantType)) data.Add(new KeyValuePair<string, string>("grant_type", request.GrantType)); if(!string.IsNullOrEmpty(request.RedirectUri)) data.Add(new KeyValuePair<string, string>("redirect_uri", request.RedirectUri)); if(!string.IsNullOrEmpty(request.Code)) data.Add(new KeyValuePair<string, string>("code", request.Code)); if(!string.IsNullOrEmpty(request.RefreshToken)) data.Add(new KeyValuePair<string, string>("refresh_token", request.RefreshToken)); if(!string.IsNullOrEmpty(request.State)) data.Add(new KeyValuePair<string, string>("state", request.State)); try { HttpResponseMessage response = null; // Post request response = _client.PostAsync(url, new FormUrlEncodedContent(data)).GetAwaiter().GetResult(); // Post response postResponse.Content = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); postResponse.StatusCode = response.StatusCode; postResponse.ReasonPhrase = response.ReasonPhrase; //Console.WriteLine("PostResponse: " + postResponse.Content); return postResponse; } catch(Exception ex) { postResponse.ReasonPhrase = ex.Message; postResponse.StatusCode = HttpStatusCode.InternalServerError; return postResponse; } } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if(disposed) return; if(disposing && _client != null) { _client.Dispose(); _client = null; } disposed = true; } } } |
AccessTokenRequest.cs
AccessTokenRequest.cs | ![]() |
---|---|
namespace FaxWSConsoleApplication { /// <summary> /// Helper class /// Stores request to get an Access Token for a user from the Identity server /// </summary> public class AccessTokenRequest { public AccessTokenRequest() { ClientId = string.Empty; ClientSecret = string.Empty; Username = string.Empty; Password = string.Empty; GrantType = string.Empty; Scope = string.Empty; RedirectUri = string.Empty; Code = string.Empty; RefreshToken = string.Empty; State = string.Empty; } public AccessTokenRequest(string clientId, string clientSecret, string username, string password, string grantType, string scope, string redirectUri, string code, string refreshToken, string state) { ClientId = clientId; ClientSecret = clientSecret; Username = username; Password = password; GrantType = grantType; Scope = scope; RedirectUri = redirectUri; Code = code; RefreshToken = refreshToken; State = state; } public string GrantType { get; set; } public string ClientId { get; set; } public string ClientSecret { get; set; } public string Scope { get; set; } public string Username { get; set; } public string Password { get; set; } public string RedirectUri { get; set; } public string Code { get; set; } public string RefreshToken { get; set; } public string State { get; set; } } } |
AccessTokenResponse.cs
AccessTokenResponse.cs | ![]() |
---|---|
using System; using System.Net; using System.Text.Json.Serialization; namespace FaxWSConsoleApplication { /// <summary> /// Helper class /// Stores the Access Token received from the Identity server /// </summary> public class AccessTokenResponse { public AccessTokenResponse() { requestDateTime = DateTime.Now; AccessToken = string.Empty; ExpiresInSeconds = 0; TokenType = string.Empty; Error = string.Empty; ErrorDescription = string.Empty; HttpStatusCode = System.Net.HttpStatusCode.OK; } public override string ToString() { string nl = Environment.NewLine; return "TokenType: " + TokenType + nl + "AccessToken: " + AccessToken + nl + "RefreshToken: " + RefreshToken + nl + "State: " + State + nl + "ExpiresInSeconds: " + ExpiresInSeconds + nl + "RequestDateTime: " + RequestDateTime + nl + "HttpStatusCode: " + HttpStatusCode + nl + "ErrorDescription: " + ErrorDescription + nl + "Error: " + Error + nl; } [JsonPropertyName("token_type")] public string TokenType { get; set; } [JsonPropertyName("access_token")] public string AccessToken { get; set; } [JsonPropertyName("expires_in")] public int ExpiresInSeconds { get; set; } [JsonPropertyName("refresh_token")] public string RefreshToken { get; set; } [JsonPropertyName("error")] public string Error { get; set; } [JsonPropertyName("error_description")] public string ErrorDescription { get; set; } public bool IsExpired { get { if(ValidFrom == null || ValidTo == null) return true; return ValidTo < DateTime.Now; } } public HttpStatusCode HttpStatusCode { get; set; } private DateTime? requestDateTime; public DateTime? RequestDateTime { get { return requestDateTime; } } public DateTime? ValidFrom { get { return (String.IsNullOrEmpty(Error) ? RequestDateTime : null); } } public DateTime? ValidTo { get { return (ExpiresInSeconds > 0 && ValidFrom.HasValue) ? ValidFrom.Value.AddSeconds(ExpiresInSeconds) : (DateTime?)null; } } [JsonPropertyName("state")] public string State { get; set; } } } |
PostResponse.cs
PostResponse.cs | ![]() |
---|---|
using System.Net; namespace FaxWSConsoleApplication { /// <summary> /// Helper class /// Holds pieces of data from HttpMessage in RestApiClient /// </summary> public class PostResponse { public string Content { get; set; } public HttpStatusCode StatusCode { get; set; } public string ReasonPhrase { get; set; } } } |
FaxWSAuthWrapper.cs
FaxWSAuthWrapper.cs | ![]() |
---|---|
using FaxWSConsoleApplication.FaxWSReference; using System; using System.Net; namespace FaxWSConsoleApplication { /// <summary> /// Wrap the SOAP interface to grab additional helpful data /// </summary> public class FaxWSAuthWrapper : FaxWS { // Request public string AccessToken = string.Empty; public string ClientRequestId = string.Empty; // Response public int ExecutionTime = -1; public string ActivityId = string.Empty; public WebHeaderCollection ResponseHeaders = null; public HttpStatusCode HttpStatusCode = HttpStatusCode.OK; /// <remarks/> public FaxWSAuthWrapper() { } protected override WebRequest GetWebRequest(Uri uri) { HttpWebRequest request = (HttpWebRequest)base.GetWebRequest(uri); // Add additional request headers if(!string.IsNullOrEmpty(ClientRequestId)) { request.Headers.Add("x-ch-request-id", ClientRequestId); } // Add Authorization header if(!string.IsNullOrEmpty(AccessToken)) { request.Headers.Add("Authorization", "Bearer " + AccessToken); } return request; } // Overwrite to store away http headers protected override WebResponse GetWebResponse(WebRequest request) { HttpWebResponse httpresponse = (HttpWebResponse)request.GetResponse(); ActivityId = string.Empty; ExecutionTime = -1; HttpStatusCode = httpresponse.StatusCode; // storeaway headers ResponseHeaders = httpresponse.Headers; string strActivityId = httpresponse.GetResponseHeader("x-ch-activity-id"); if(!string.IsNullOrEmpty(strActivityId)) { ActivityId = strActivityId; } string strResponseTime = httpresponse.GetResponseHeader("x-ch-execution-time"); if(!string.IsNullOrEmpty(strResponseTime)) { ExecutionTime = Convert.ToInt32(strResponseTime); } // call base class return base.GetWebResponse(request); } protected override WebResponse GetWebResponse(WebRequest request, IAsyncResult ar) { HttpWebResponse httpresponse = (HttpWebResponse)request.GetResponse(); ActivityId = string.Empty; ExecutionTime = -1; HttpStatusCode = httpresponse.StatusCode; // storeaway headers ResponseHeaders = httpresponse.Headers; string strActivityId = httpresponse.GetResponseHeader("x-ch-activity-id"); if(!string.IsNullOrEmpty(strActivityId)) { ActivityId = strActivityId; } string strResponseTime = httpresponse.GetResponseHeader("x-ch-execution-time"); if(!string.IsNullOrEmpty(strResponseTime)) { ExecutionTime = Convert.ToInt32(strResponseTime); } // call base class return base.GetWebResponse(request, ar); } } } |