using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; using Sony.Filtr.Contracts.Abstractions; using Sony.Filtr.Contracts.Entities; using Sony.Filtr.SpotifyWebAPI.Model; namespace Sony.Filtr.SpotifyWebAPI { public class SpotifyAuthorizationCodeHttpMessageHandler : DelegatingHandler { private ServiceAccount _serviceAccount; private readonly IServiceAccountManager _serviceAccountManager; private readonly string _clientId; private readonly string _clientSecret; public SpotifyAuthorizationCodeHttpMessageHandler(ServiceAccount serviceAccount, IServiceAccountManager serviceAccountManager, string clientId, string clientSecret, HttpMessageHandler httpMessageHandler) : base(httpMessageHandler) { _serviceAccount = serviceAccount; _serviceAccountManager = serviceAccountManager; _clientId = clientId; _clientSecret = clientSecret; } protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { if (request.Headers.Authorization == null) { request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", await GetAuthenticationTokenAsync()); } var response = await base.SendAsync(request, cancellationToken); if (response.StatusCode == HttpStatusCode.Unauthorized) { //Our access code be old, try and refresh var refreshedAccessToken = await RequestRefreshedAccessToken(_serviceAccount.RefreshToken); if (refreshedAccessToken == null || string.IsNullOrWhiteSpace(refreshedAccessToken.access_token)) { throw new Exception("Could not get access token by refresh token"); } _serviceAccount = SaveAccessToken(_serviceAccount, refreshedAccessToken); //Try request once more with updated access token request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", await GetAuthenticationTokenAsync()); response = await base.SendAsync(request, cancellationToken); } return response; } private async Task GetAuthenticationTokenAsync() { if (string.IsNullOrWhiteSpace(_serviceAccount.AccessToken)) { throw new Exception("No access token for service account."); } if (_serviceAccount.AccessTokenExpiry.HasValue) { var expiryDatetime = _serviceAccount.UpdatedDate.AddSeconds(_serviceAccount.AccessTokenExpiry.Value); var safetyMargin = TimeSpan.FromSeconds(-10); if (expiryDatetime < DateTime.Now.Add(safetyMargin)) { var refreshedAccessToken = await RequestRefreshedAccessToken(_serviceAccount.RefreshToken); if (refreshedAccessToken == null || string.IsNullOrWhiteSpace(refreshedAccessToken.access_token)) { throw new Exception("Could not get access token by refresh token"); } _serviceAccount = SaveAccessToken(_serviceAccount, refreshedAccessToken); } } return _serviceAccount.AccessToken; } private ServiceAccount SaveAccessToken(ServiceAccount serviceAccount, AutenticationResponse refreshedAccessToken) { _serviceAccount.UpdatedDate = DateTime.Now; _serviceAccount.AccessToken = refreshedAccessToken.access_token; _serviceAccount.AccessTokenExpiry = refreshedAccessToken.expires_in; var updatedServiceAccount = _serviceAccountManager.SaveServiceAccount(serviceAccount); return updatedServiceAccount; } private async Task RequestRefreshedAccessToken(string refreshToken) { HttpClient client = new HttpClient(); var contentParams = new Dictionary() { {"grant_type", "refresh_token"}, {"refresh_token", refreshToken}, }; var content = new FormUrlEncodedContent(contentParams); var authHeader = Base64Encode(_clientId + ":" + _clientSecret); var requestMessage = new HttpRequestMessage(HttpMethod.Post, "https://accounts.spotify.com/api/token"); requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Basic", authHeader); requestMessage.Content = content; var response = await client.SendAsync(requestMessage); var authenticationResponse = await response.Content.ReadAsAsync(); return authenticationResponse; } public static string Base64Encode(string plainText) { var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText); return Convert.ToBase64String(plainTextBytes); } } }