public async Task <AuthenticateResult> AuthenticateAsync(HttpRequestBase request, HttpResponseBase response) { request.ThrowIfNull("request"); response.ThrowIfNull("response"); if (_authenticationProvider == null) { return(AuthenticateResult.NoAuthenticationPerformed()); } AuthenticationResult result = await _authenticationProvider.AuthenticateAsync(request, response, this); return(result == AuthenticationResult.AuthenticationSucceeded ? AuthenticateResult.AuthenticationSucceeded() : AuthenticateResult.AuthenticationFailed(await _authenticationProvider.GetFailedAuthenticationResponseAsync(request))); }
public async Task <ClaimsIdentity> AuthenticateAsync(HttpRequest httpRequest, HttpResponse httpResponse) { if (httpRequest == null) { throw new ArgumentNullException(nameof(httpRequest)); } if (httpResponse == null) { throw new ArgumentNullException(nameof(httpResponse)); } var authorizationHeader = httpRequest.Headers["Authorization"]; if (string.IsNullOrWhiteSpace(authorizationHeader)) { httpResponse.StatusCode = (int)HttpStatusCode.Unauthorized; return(null); } var claimsIdentity = await _authenticationProvider.AuthenticateAsync(authorizationHeader).ConfigureAwait(false); if (claimsIdentity == null) { httpResponse.StatusCode = (int)HttpStatusCode.Unauthorized; return(null); } var appIdClaimName = AuthHelpers.GetAppIdClaimName(claimsIdentity); var appId = claimsIdentity.Claims.FirstOrDefault(c => c.Type == appIdClaimName)?.Value; if (_whitelistAuthenticationProvider.AppsWhitelist != null && _whitelistAuthenticationProvider.AppsWhitelist.Count > 0 && !_whitelistAuthenticationProvider.AppsWhitelist.Contains(appId)) { httpResponse.StatusCode = (int)HttpStatusCode.Unauthorized; await httpResponse.WriteAsync("Skill could not allow access from calling bot.").ConfigureAwait(false); } return(claimsIdentity); }
public async Task <HttpResponseMessage> InvokeAsync(HttpMethod verb, string resource, Dictionary <string, string> queryParams = null, Dictionary <string, object> headers = null, Func <CancellationToken, Task <HttpContent> > contentFactory = null, bool bufferResponseContent = true, bool skipAuthentication = false, CancellationToken cancellationToken = default) { if (verb == null) { throw new ArgumentNullException(nameof(verb)); } if (resource == null) { throw new ArgumentNullException(nameof(resource)); } // Performs a maximum of as many attempts as the number of configured base API endpoints, keeping track // of the last used endpoint after each call, in order to try to distribute the load evenly across the // available endpoints. var errors = new Dictionary <Url, Exception>(); for (var idxAttempt = 0; idxAttempt < _baseUrls.Length; idxAttempt++, _currentBaseUrlIdx++) { cancellationToken.ThrowIfCancellationRequested(); // Authenticates the underlying flurl client, if needed if (!skipAuthentication) { await _authenticator.AuthenticateAsync(this, cancellationToken) .ConfigureAwait(false); } // Build the final url by combining the base url and the specified path and query var baseUrl = _baseUrls[_currentBaseUrlIdx % _baseUrls.Length]; var finalUrl = new Url(baseUrl) .AppendPathSegment(resource); // Add the eventual querystring parameters if (queryParams != null) { foreach (var queryParam in queryParams) { finalUrl = finalUrl.SetQueryParam(queryParam.Key, queryParam.Value); } } var request = UnderlyingClient.Request(finalUrl); // Add the eventual custom headers if (headers != null) { foreach (var queryParam in headers) { request = request.WithHeader(queryParam.Key, queryParam.Value); } } try { // HttpClient.SendAsync() disposed the passed content automatically: https://github.com/dotnet/runtime/issues/14612 // A fix was made (https://github.com/dotnet/corefx/pull/19082) but Flurl still targets .NET Standard and not .NET Core, // so we can't take advantage of it: because of that, we are using a factory to build the actual content. HttpContent content = null; HttpResponseMessage response; try { if (contentFactory != null) { content = await contentFactory(cancellationToken) .ConfigureAwait(false); } response = await request .SendAsync(verb, content, cancellationToken, bufferResponseContent ?HttpCompletionOption.ResponseContentRead : HttpCompletionOption.ResponseHeadersRead) .ConfigureAwait(false); } finally { // Dispose the eventual content - that should make sense whenever .NET Standard will receive the fix mentioned above // or whenever Flurl will target .NET Core. content?.Dispose(); } // Automatically retry with another host on HTTP 5xx status codes if ((int)response.StatusCode >= 500 && (int)response.StatusCode <= 599) { errors.Add(finalUrl, new EndpointServerErrorException($"The API endpoint {baseUrl} returned a server error HTTP status code {response.StatusCode}.")); continue; } // If the request is unauthorized, give the authentication provider a chance to remediate (on a subsequent attempt) if (response.StatusCode == HttpStatusCode.Unauthorized) { await _authenticator.HandleUnauthorizedRequestAsync(this, cancellationToken) .ConfigureAwait(false); errors.Add(finalUrl, new AuthorizationException("Can't authenticate to Verifalia using the provided credentials (will retry in the next attempt).")); continue; } // Fails on the first occurrence of an HTTP 403 status code if (response.StatusCode == HttpStatusCode.Forbidden) { throw new AuthorizationException(response.ReasonPhrase); } // Returns the original response only if it has been completed with a non-500 HTTP status code return(response); } catch (AggregateException aggregateException) { var flattenException = aggregateException.Flatten(); foreach (var innerException in flattenException.InnerExceptions) { if (innerException is OperationCanceledException) { throw innerException; } errors.Add(finalUrl, innerException); } } catch (FlurlHttpTimeoutException httpException) { // A single Flurl request timed out - we take note of it and proceed with the next endpoint, if any errors.Add(finalUrl, httpException); } catch (FlurlHttpException httpException) { if (httpException.InnerException is OperationCanceledException && cancellationToken.IsCancellationRequested) { throw httpException.InnerException; } errors.Add(finalUrl, httpException); } } throw new ServiceUnreachableException("All the base URIs are unreachable: " + String.Join(", ", errors.Select(error => $"{error.Key} => {error.Value.Message}")), new AggregateException(errors.Select(error => error.Value))); }