This topic contains the following sections.
TokenManager.cs
TokenManager.cs | ![]() |
---|---|
using System; using System.Runtime.Caching; namespace IWSConsoleSample { /// <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 string key = "tokenresponse_"; private 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 IWSConsoleSample { /// <summary> /// Helper class /// Talks with the Identity server /// </summary> public class RestApiClient : IDisposable { #region Variables protected readonly string _baseAddress; protected readonly string _tokenUrl; protected readonly string _authorizeUrl; protected HttpClient _client = null; #endregion public RestApiClient(string endpoint) { _baseAddress = endpoint; //_authorizeUrl = _baseAddress + "connect/authorize"; _tokenUrl = "connect/token"; _client = new HttpClient(); _client.BaseAddress = new Uri(endpoint); } public AccessTokenResponse GetToken(AccessTokenRequest request) { AccessTokenResponse response = new AccessTokenResponse(); var postResponse = PostData(_tokenUrl, 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 relativeUrl, 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(relativeUrl, 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() { if (_client != null) { _client.Dispose(); _client = null; } } } } |
PostResponse.cs
PostResponse.cs | ![]() |
---|---|
using System.Net; namespace IWSConsoleSample { /// <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; } } } |
AccessTokenRequest.cs
AccessTokenRequest.cs | ![]() |
---|---|
namespace IWSConsoleSample { /// <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 = null, string code = null, string refreshToken = null, string state = null) { 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 IWSConsoleSample { /// <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; } public DateTime? RequestDateTime { get; } public DateTime? ValidFrom { get { return (String.IsNullOrEmpty(Error) ? RequestDateTime : (DateTime?)null); } } public DateTime? ValidTo { get { return (ExpiresInSeconds > 0 && ValidFrom.HasValue) ? ValidFrom.Value.AddSeconds(ExpiresInSeconds) : (DateTime?)null; } } [JsonPropertyName("state")] public string State { get; set; } } } |
IWSAuthWrapper.cs
IWSAuthWrapper.cs | ![]() |
---|---|
using IWSConsoleSample.IWSServiceReference; using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Text; using System.Threading.Tasks; namespace IWSConsoleSample { public class IWSAuthWrapper : InboundWS { // 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 IWSAuthWrapper() { } 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); } } } |