using Infiniti.Interfaces.Ebay; using Infiniti.Models.Configuration; using Infiniti.Models.Ebay; using Infiniti.Models.Logging; using Infiniti.Models.Tokens; using Infiniti.Models.Users; using Infiniti.Standard; using Newtonsoft.Json; using System; using System.Net.Http; using System.Net.Http.Headers; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Web; namespace Infiniti.Services.eBay { public class PayPalAuthService : ServiceBase, IPayPalAuthService { private IHttpClientFactory _clientFactory; public PayPalAuthService(IEventPublisher eventPublisher, IHttpClientFactory clientFactory) : base(eventPublisher) => _clientFactory = clientFactory; public async Task PayPalAuthUrlAsync(PayPalAuthUrlRequest request) { var authURIPath = await eventPublisher.GetApplicationSettingAsync("PayPalAuthCodeGrant").ConfigureAwait(false); var client_id = await eventPublisher.GetApplicationSettingAsync("PayPalAuthClientId").ConfigureAwait(false); var redirectUri = await eventPublisher.GetApplicationSettingAsync("PayPalAuthClientRedirect").ConfigureAwait(false); // name not a uri path var scope = HttpUtility.UrlEncode(await eventPublisher.GetApplicationSettingAsync("PayPalAuthClientScope").ConfigureAwait(false)); var magicUuid = Guid.NewGuid().ToString(); await eventPublisher.SaveTokenAsync(request.User, "PayPalAuthClientMagic", magicUuid, request.ClientId).ConfigureAwait(true); var uaURL = $"{authURIPath}?response_type=code&client_id={client_id}&scope={scope}&state={magicUuid}&redirect_uri={redirectUri}"; return new PayPalAuthUrlResponse(request, uaURL); } public async Task PayPalAuthCallbackAsync(PayPalAuthCallbackRequest request) { try { var avaskClientId = request.ClientId; var clientMagic = await eventPublisher.GetTokenAsync(request.User, "PayPalAuthClientMagic", avaskClientId).ConfigureAwait(false); if (clientMagic != request.PayPalAuthCallbackGet.state) throw new ApplicationException("Error: Bad Magic number"); var oneTimeCode = request.PayPalAuthCallbackGet.code; if (oneTimeCode == "") throw new ApplicationException("Error: missing 'one time code'"); var redirect_uri = await eventPublisher.GetApplicationSettingAsync("PayPalAuthClientRedirect").ConfigureAwait(false); var authContent = $"grant_type=authorization_code&redirect_uri={redirect_uri}&code={oneTimeCode}"; var response = await AuthCommonAsync(authContent, request.User).ConfigureAwait(false); if (response == "") throw new ApplicationException("Error: unable to Aquire Tokens"); var authReturn = JsonConvert.DeserializeObject(response); await eventPublisher.SaveTokenAsync(request.User, "PayPalAuthAccessToken", authReturn.access_token, avaskClientId).ConfigureAwait(false); return new PayPalAuthCallbackResponse(request, authReturn); } catch (Exception ex) { return new PayPalAuthCallbackResponse(request, null, ex); } } private async Task AuthCommonAsync(string contentPost, User user) { // POST var funcName = nameof(AuthCommonAsync); var domainURI = await eventPublisher.GetApplicationSettingAsync("PayPalAuthBaseURL").ConfigureAwait(false); var tokenPath = await eventPublisher.GetApplicationSettingAsync("PayPalAuthTokenPath").ConfigureAwait(false); var client_secret = await eventPublisher.GetApplicationSettingAsync("PayPalAuthClientSecret").ConfigureAwait(false); var client_id = await eventPublisher.GetApplicationSettingAsync("PayPalAuthClientId").ConfigureAwait(false); var encodedKey = Convert.ToBase64String(ASCIIEncoding.ASCII.GetBytes($"{client_id}:{client_secret}")); var clientTimeout = await eventPublisher.GetApplicationSettingIntAsync("OAuthHttpTimeout").ConfigureAwait(false); var clientRetries = await eventPublisher.GetApplicationSettingIntAsync("OAuthHttpRetries").ConfigureAwait(false); var content = ""; var client = _clientFactory.CreateClient(); var loop = 0; do { try { var request = new HttpRequestMessage(HttpMethod.Post, $"{domainURI}{tokenPath}"); request.Content = new StringContent(contentPost, Encoding.UTF8, "application/x-www-form-urlencoded"); request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); request.Headers.Authorization = new AuthenticationHeaderValue("Basic", encodedKey); var cts = new CancellationTokenSource(TimeSpan.FromSeconds(clientTimeout)); var response = await client.SendAsync(request, cts.Token).ConfigureAwait(false); content = await response.Content.ReadAsStringAsync().ConfigureAwait(false); if (response.IsSuccessStatusCode) { return content; } if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized) { await eventPublisher.LogInformationAsync(user, $"{funcName}: Authorisation Failure '{content}'.").ConfigureAwait(false); throw new UnauthorizedAccessException($"Error: Bad Authentication Failure: '{content}'"); } if (response.StatusCode == System.Net.HttpStatusCode.BadRequest) { await eventPublisher.LogInformationAsync(user, $"{funcName}: User Bad Request '{content}'.").ConfigureAwait(false); throw new ArgumentException($"Fault: User Invalid Request '{content}'"); } throw new ApplicationException($"Bad Response Exception: '{content}'"); } catch (ApplicationException ex) { await eventPublisher.LogInformationAsync(user, $"{funcName}: '{ex.Message}'.").ConfigureAwait(false); if (loop < clientRetries) await Task.Delay(client.Timeout).ConfigureAwait(false); } catch (HttpRequestException ex) { await eventPublisher.LogInformationAsync(user, $"{funcName}: Communication Exception '{ex.Message}'.").ConfigureAwait(false); if (loop < clientRetries) await Task.Delay(client.Timeout).ConfigureAwait(false); } catch (TaskCanceledException ex) { await eventPublisher.LogInformationAsync(user, $"{funcName}: Timeout Exception '{ex.Message}'.").ConfigureAwait(false); } catch (Exception ex) { await eventPublisher.LogInformationAsync(user, $"{funcName}: General Exception '{ex.Message}'.").ConfigureAwait(false); throw; } ++loop; } while (loop <= clientRetries); await eventPublisher.LogInformationAsync(user, $"{funcName}: No Response from Target: '{content}'.").ConfigureAwait(false); throw new ApplicationException("Fault: No Response from Target."); } } }