using System; using System.Net; using System.Net.Http; using System.Threading.Tasks; namespace Microsoft.Translator.Samples { /// /// Client to call Cognitive Services Azure Auth Token service in order to get an access token. /// Exposes asynchronous as well as synchronous methods. /// public class AzureAuthToken { /// URL of the token service private static readonly Uri ServiceUrl = new Uri("https://api.cognitive.microsoft.com/sts/v1.0/issueToken"); /// Name of header used to pass the subscription key to the token service private const string OcpApimSubscriptionKeyHeader = "Ocp-Apim-Subscription-Key"; /// After obtaining a valid token, this class will cache it for this duration. /// Use a duration of 5 minutes, which is less than the actual token lifetime of 10 minutes. private static readonly TimeSpan TokenCacheDuration = new TimeSpan(0, 5, 0); /// Cache the value of the last valid token obtained from the token service. private string _storedTokenValue = string.Empty; /// When the last valid token was obtained. private DateTime _storedTokenTime = DateTime.MinValue; /// Gets the subscription key. public string SubscriptionKey { get; } /// Gets the HTTP status code for the most recent request to the token service. public HttpStatusCode RequestStatusCode { get; private set; } /// /// Creates a client to obtain an access token. /// /// Subscription key to use to get an authentication token. public AzureAuthToken(string key) { if (string.IsNullOrEmpty(key)) { throw new ArgumentNullException(nameof(key), "A subscription key is required"); } this.SubscriptionKey = key; this.RequestStatusCode = HttpStatusCode.InternalServerError; } /// /// Gets a token for the specified subscription. /// /// The encoded JWT token prefixed with the string "Bearer ". /// /// This method uses a cache to limit the number of request to the token service. /// A fresh token can be re-used during its lifetime of 10 minutes. After a successful /// request to the token service, this method caches the access token. Subsequent /// invocations of the method return the cached token for the next 5 minutes. After /// 5 minutes, a new token is fetched from the token service and the cache is updated. /// public async Task GetAccessTokenAsync() { if (string.IsNullOrWhiteSpace(this.SubscriptionKey)) { return string.Empty; } // Re-use the cached token if there is one. if ((DateTime.Now - _storedTokenTime) < TokenCacheDuration) { return _storedTokenValue; } using (var client = new HttpClient()) using (var request = new HttpRequestMessage()) { request.Method = HttpMethod.Post; request.RequestUri = ServiceUrl; request.Content = new StringContent(string.Empty); request.Headers.TryAddWithoutValidation(OcpApimSubscriptionKeyHeader, this.SubscriptionKey); client.Timeout = TimeSpan.FromSeconds(30); var response = await client.SendAsync(request); this.RequestStatusCode = response.StatusCode; response.EnsureSuccessStatusCode(); var token = await response.Content.ReadAsStringAsync(); _storedTokenTime = DateTime.Now; _storedTokenValue = "Bearer " + token; return _storedTokenValue; } } /// /// Gets a token for the specified subscription. Synchronous version. /// Use of async version preferred /// /// The encoded JWT token prefixed with the string "Bearer ". /// /// This method uses a cache to limit the number of request to the token service. /// A fresh token can be re-used during its lifetime of 10 minutes. After a successful /// request to the token service, this method caches the access token. Subsequent /// invocations of the method return the cached token for the next 5 minutes. After /// 5 minutes, a new token is fetched from the token service and the cache is updated. /// public string GetAccessToken() { // Re-use the cached token if there is one. if ((DateTime.Now - _storedTokenTime) < TokenCacheDuration) { return _storedTokenValue; } string accessToken = null; var task = Task.Run(async () => { accessToken = await this.GetAccessTokenAsync(); }); while (!task.IsCompleted) { System.Threading.Thread.Yield(); } if (task.IsFaulted) { throw task.Exception; } if (task.IsCanceled) { throw new Exception("Timeout obtaining access token."); } return accessToken; } } }