using System; using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Text; using System.Threading; using System.Threading.Tasks; using Sony.Filtr.Contracts.Abstractions; using Sony.Filtr.Contracts.Entities; namespace Sony.Filtr.YouTube.Api { public class YouTubeOAuthHttpMessageHandler : DelegatingHandler { private ServiceAccount _serviceAccount; private readonly IServiceAccountManager _serviceAccountManager; private readonly GoogleOAuth2Api _googleOAuth2; public YouTubeOAuthHttpMessageHandler(ServiceAccount serviceAccount, IServiceAccountManager serviceAccountManager, GoogleOAuth2Api googleOAuth2, HttpMessageHandler innerHttpMessageHandler) : base(innerHttpMessageHandler) { _serviceAccount = serviceAccount; _serviceAccountManager = serviceAccountManager; _googleOAuth2 = googleOAuth2; } 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.UtcNow.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, GoogleOAuth2Api.AccessTokenResponse refreshedAccessToken) { _serviceAccount.UpdatedDate = DateTime.UtcNow; _serviceAccount.AccessToken = refreshedAccessToken.access_token; if (refreshedAccessToken.expires_in != null) { int expireIn; if (int.TryParse(refreshedAccessToken.expires_in, out expireIn)) _serviceAccount.AccessTokenExpiry = expireIn; } var updatedServiceAccount = _serviceAccountManager.SaveServiceAccount(serviceAccount); return updatedServiceAccount; } private async Task RequestRefreshedAccessToken(string refreshToken) { return await _googleOAuth2.RequestRefreshedAccessTokenAsync(refreshToken); } public static string Base64Encode(string plainText) { var plainTextBytes = Encoding.UTF8.GetBytes(plainText); return Convert.ToBase64String(plainTextBytes); } } }