using System; using System.Collections.Generic; using System.Net.Http; using System.Net.Http.Headers; using System.Runtime.Caching; using System.Text; using System.Threading; using System.Threading.Tasks; using Newtonsoft.Json; using Sony.Filtr.SpotifyWebAPI.Model; namespace Sony.Filtr.SpotifyWebAPI { public class SpotifyAuthClientCredentialsHttpMessageHandler : DelegatingHandler { private readonly string _clientId; private readonly string _clientSecret; private static readonly SemaphoreSlim SyncLock = new SemaphoreSlim(1); HttpClient tokenClient; public SpotifyAuthClientCredentialsHttpMessageHandler(string clientId, string clientSecret, HttpMessageHandler httpMessageHandler): base(httpMessageHandler) { _clientId = clientId; _clientSecret = clientSecret; this.tokenClient = CreateTokenHttpClient(); } protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { if (request.Headers.Authorization == null) { request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", await GetAuthenticationTokenAsync()); } return await base.SendAsync(request, cancellationToken); } private async Task GetAuthenticationTokenAsync() { var cacheKey = "SpotifyWebApiSession-Token" + _clientId; //Locking for token? var token = MemoryCache.Default.Get(cacheKey) as string; if (token == null) { await SyncLock.WaitAsync(); try { token = MemoryCache.Default.Get(cacheKey) as string; if (token == null) { token = await InitToken(cacheKey); } } finally { SyncLock.Release(); } } return token; } private async Task InitToken(string cacheKey) { var timeBeforeRequest = DateTime.Now; var response = await GetAuthenticationTokenResponse(); var token = response.access_token; var expireTime = timeBeforeRequest.AddSeconds(Convert.ToInt32(GetTokenCacheExpirationSeconds(response.expires_in))); MemoryCache.Default.Set(cacheKey, token, new DateTimeOffset(expireTime)); return token; } private static int GetTokenCacheExpirationSeconds(int spotifyExpiresInSeconds) { return spotifyExpiresInSeconds - Math.Min((int)(spotifyExpiresInSeconds / 10), 10); } private static string Base64Encode(string plainText) { var plainTextBytes = Encoding.UTF8.GetBytes(plainText); return Convert.ToBase64String(plainTextBytes); } private HttpClient CreateTokenHttpClient() { HttpClient client = new HttpClient(); var authHeader = Base64Encode(_clientId + ":" + _clientSecret); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", authHeader); return client; } private async Task GetAuthenticationTokenResponse() { var content = new FormUrlEncodedContent(new[] { new KeyValuePair("grant_type", "client_credentials") }); var response = await this.tokenClient.PostAsync("https://accounts.spotify.com/api/token", content); var responseString = await response.Content.ReadAsStringAsync(); var authenticationResponse = JsonConvert.DeserializeObject(responseString); return authenticationResponse; } } }