public void MsalServiceException_FromHttpResponse() { // Arrange string responseBody = "body"; var statusCode = HttpStatusCode.BadRequest; var retryAfterSpan = new TimeSpan(3600); var httpResponse = new HttpResponseMessage(statusCode) { Content = new StringContent(responseBody) }; httpResponse.Headers.RetryAfter = new RetryConditionHeaderValue(retryAfterSpan); var coreResponse = HttpManager.CreateResponseAsync(httpResponse).Result; // Act var msalException = MsalExceptionFactory.GetServiceException(ExCode, ExMessage, coreResponse); // Assert var msalServiceException = msalException as MsalServiceException; Assert.AreEqual(ExCode, msalServiceException.ErrorCode); Assert.AreEqual(responseBody, msalServiceException.ResponseBody); Assert.AreEqual(ExMessage, msalServiceException.Message); Assert.AreEqual((int)statusCode, msalServiceException.StatusCode); Assert.AreEqual(retryAfterSpan, msalServiceException.Headers.RetryAfter.Delta); }
/// <inheritdoc/> public async Task <MexDocument> GetMexDocumentAsync(string federationMetadataUrl, RequestContext requestContext) { var uri = new UriBuilder(federationMetadataUrl); HttpResponse httpResponse = await _httpManager.SendGetAsync(uri.Uri, null, requestContext).ConfigureAwait(false); if (httpResponse.StatusCode != System.Net.HttpStatusCode.OK) { throw MsalExceptionFactory.GetServiceException( CoreErrorCodes.AccessingWsMetadataExchangeFailed, string.Format(CultureInfo.CurrentCulture, CoreErrorMessages.HttpRequestUnsuccessful, (int)httpResponse.StatusCode, httpResponse.StatusCode), new ExceptionDetail() { StatusCode = (int)httpResponse.StatusCode, ServiceErrorCodes = new[] { httpResponse.StatusCode.ToString() } }); } var mexDoc = new MexDocument(httpResponse.Body); requestContext.Logger.InfoPii( $"MEX document fetched and parsed from '{federationMetadataUrl}'", "Fetched and parsed MEX"); return(mexDoc); }
protected override async Task <string> GetOpenIdConfigurationEndpointAsync( string userPrincipalName, RequestContext requestContext) { if (ValidateAuthority) { var drsResponse = await GetMetadataFromEnrollmentServerAsync( userPrincipalName, requestContext) .ConfigureAwait(false); if (!string.IsNullOrEmpty(drsResponse.Error)) { MsalExceptionFactory.GetServiceException( drsResponse.Error, drsResponse.ErrorDescription, ExceptionDetail.FromDrsResponse(drsResponse)); } if (drsResponse.IdentityProviderService?.PassiveAuthEndpoint == null) { throw MsalExceptionFactory.GetServiceException( CoreErrorCodes.MissingPassiveAuthEndpoint, CoreErrorMessages.CannotFindTheAuthEndpont, ExceptionDetail.FromDrsResponse(drsResponse)); } string resource = string.Format(CultureInfo.InvariantCulture, CanonicalAuthority); string webfingerUrl = string.Format( CultureInfo.InvariantCulture, "https://{0}/adfs/.well-known/webfinger?rel={1}&resource={2}", drsResponse.IdentityProviderService.PassiveAuthEndpoint.Host, DefaultRealm, resource); var httpResponse = await ServiceBundle.HttpManager.SendGetAsync(new Uri(webfingerUrl), null, requestContext).ConfigureAwait(false); if (httpResponse.StatusCode != HttpStatusCode.OK) { throw MsalExceptionFactory.GetServiceException( CoreErrorCodes.InvalidAuthority, CoreErrorMessages.AuthorityValidationFailed, httpResponse); } var wfr = OAuth2Client.CreateResponse <AdfsWebFingerResponse>(httpResponse, requestContext, false); if (wfr.Links.FirstOrDefault( a => a.Rel.Equals(DefaultRealm, StringComparison.OrdinalIgnoreCase) && a.Href.Equals(resource, StringComparison.OrdinalIgnoreCase)) == null) { throw MsalExceptionFactory.GetClientException( CoreErrorCodes.InvalidAuthority, CoreErrorMessages.InvalidAuthorityOpenId); } } return(GetDefaultOpenIdConfigurationEndpoint()); }
public void ParamValidation() { AssertException.Throws <ArgumentNullException>(() => MsalExceptionFactory.GetClientException(null, ExMessage)); AssertException.Throws <ArgumentNullException>(() => MsalExceptionFactory.GetClientException("", ExMessage)); AssertException.Throws <ArgumentNullException>( () => MsalExceptionFactory.GetServiceException(ExCode, "", new ExceptionDetail())); AssertException.Throws <ArgumentNullException>( () => MsalExceptionFactory.GetServiceException(ExCode, null, new ExceptionDetail())); }
/// <inheritdoc/> public async Task <WsTrustResponse> GetWsTrustResponseAsync( WsTrustEndpoint wsTrustEndpoint, string wsTrustRequest, RequestContext requestContext) { var headers = new Dictionary <string, string> { { "ContentType", "application/soap+xml" }, { "SOAPAction", (wsTrustEndpoint.Version == WsTrustVersion.WsTrust2005) ? XmlNamespace.Issue2005.ToString() : XmlNamespace.Issue.ToString() } }; var body = new StringContent( wsTrustRequest, Encoding.UTF8, headers["ContentType"]); IHttpWebResponse resp = await _httpManager.SendPostForceResponseAsync(wsTrustEndpoint.Uri, headers, body, requestContext).ConfigureAwait(false); if (resp.StatusCode != System.Net.HttpStatusCode.OK) { string errorMessage = null; try { errorMessage = WsTrustResponse.ReadErrorResponse(XDocument.Parse(resp.Body, LoadOptions.None), requestContext); } catch (System.Xml.XmlException) { errorMessage = resp.Body; } throw MsalExceptionFactory.GetServiceException( CoreErrorCodes.FederatedServiceReturnedError, string.Format( CultureInfo.CurrentCulture, CoreErrorMessages.FederatedServiceReturnedErrorTemplate, wsTrustEndpoint.Uri, errorMessage), new ExceptionDetail() { StatusCode = (int)resp.StatusCode, ResponseBody = resp.Body, HttpResponseHeaders = resp.Headers }); } try { return(WsTrustResponse.CreateFromResponse(resp.Body, wsTrustEndpoint.Version)); } catch (System.Xml.XmlException ex) { throw MsalExceptionFactory.GetClientException( CoreErrorCodes.ParsingWsTrustResponseFailed, CoreErrorCodes.ParsingWsTrustResponseFailed, ex); } }
public static void CreateErrorResponse(HttpResponse response, RequestContext requestContext) { bool shouldLogAsError = true; Exception serviceEx; try { var msalTokenResponse = JsonHelper.DeserializeFromJson <MsalTokenResponse>(response.Body); if (CoreErrorCodes.InvalidGrantError.Equals(msalTokenResponse.Error, StringComparison.OrdinalIgnoreCase)) { throw MsalExceptionFactory.GetUiRequiredException( CoreErrorCodes.InvalidGrantError, msalTokenResponse.ErrorDescription, null, ExceptionDetail.FromHttpResponse(response)); } serviceEx = MsalExceptionFactory.GetServiceException( msalTokenResponse.Error, msalTokenResponse.ErrorDescription, response); // For device code flow, AuthorizationPending can occur a lot while waiting // for the user to auth via browser and this causes a lot of error noise in the logs. // So suppress this particular case to an Info so we still see the data but don't // log it as an error since it's expected behavior while waiting for the user. if (string.Compare(msalTokenResponse.Error, OAuth2Error.AuthorizationPending, StringComparison.OrdinalIgnoreCase) == 0) { shouldLogAsError = false; } } catch (SerializationException ex) { serviceEx = MsalExceptionFactory.GetClientException(CoreErrorCodes.UnknownError, response.Body, ex); } if (shouldLogAsError) { requestContext.Logger.ErrorPii(serviceEx); } else { requestContext.Logger.InfoPii(serviceEx); } throw serviceEx; }
public void MsalServiceException() { // Arrange string claims = "claims"; string responseBody = "responseBody"; int statusCode = 509; string innerExMsg = "innerExMsg"; var innerException = new NotImplementedException(innerExMsg); // Act var msalException = MsalExceptionFactory.GetServiceException( ExCode, ExMessage, innerException, new ExceptionDetail { Claims = claims, ResponseBody = responseBody, StatusCode = statusCode }); // Assert var msalServiceException = msalException as MsalServiceException; Assert.AreEqual(innerException, msalServiceException.InnerException); Assert.AreEqual(ExCode, msalServiceException.ErrorCode); Assert.AreEqual(claims, msalServiceException.Claims); Assert.AreEqual(responseBody, msalServiceException.ResponseBody); Assert.AreEqual(ExMessage, msalServiceException.Message); Assert.AreEqual(statusCode, msalServiceException.StatusCode); // Act string piiMessage = MsalExceptionFactory.GetPiiScrubbedDetails(msalException); // Assert Assert.IsFalse(string.IsNullOrEmpty(piiMessage)); Assert.IsTrue( piiMessage.Contains(typeof(MsalServiceException).Name), "The pii message should contain the exception type"); Assert.IsTrue( piiMessage.Contains(typeof(NotImplementedException).Name), "The pii message should have the inner exception type"); Assert.IsTrue(piiMessage.Contains(ExCode)); Assert.IsFalse(piiMessage.Contains(ExMessage)); Assert.IsFalse(piiMessage.Contains(innerExMsg)); }
private static Exception ExtractErrorsFromTheResponse(HttpResponse response, ref bool shouldLogAsError) { Exception exceptionToThrow = null; // In cases where the end-point is not found (404) response.body will be empty. if (string.IsNullOrWhiteSpace(response.Body)) { return(null); } var msalTokenResponse = JsonHelper.DeserializeFromJson <MsalTokenResponse>(response.Body); if (msalTokenResponse?.Error == null) { return(null); } if (CoreErrorCodes.InvalidGrantError.Equals(msalTokenResponse.Error, StringComparison.OrdinalIgnoreCase)) { exceptionToThrow = MsalExceptionFactory.GetUiRequiredException( CoreErrorCodes.InvalidGrantError, msalTokenResponse.ErrorDescription, null, ExceptionDetail.FromHttpResponse(response)); } else { exceptionToThrow = MsalExceptionFactory.GetServiceException( msalTokenResponse.Error, msalTokenResponse.ErrorDescription, response); } // For device code flow, AuthorizationPending can occur a lot while waiting // for the user to auth via browser and this causes a lot of error noise in the logs. // So suppress this particular case to an Info so we still see the data but don't // log it as an error since it's expected behavior while waiting for the user. if (string.Compare(msalTokenResponse.Error, OAuth2Error.AuthorizationPending, StringComparison.OrdinalIgnoreCase) == 0) { shouldLogAsError = false; } return(exceptionToThrow); }
public static void CreateErrorResponse(HttpResponse response, RequestContext requestContext) { bool shouldLogAsError = true; var httpErrorCodeMessage = string.Format(CultureInfo.InvariantCulture, "HttpStatusCode: {0}: {1}", (int)response.StatusCode, response.StatusCode.ToString()); requestContext.Logger.Info(httpErrorCodeMessage); Exception exceptionToThrow = null; try { exceptionToThrow = ExtractErrorsFromTheResponse(response, ref shouldLogAsError); } catch (SerializationException) // in the rare case we get an error response we cannot deserialize { exceptionToThrow = MsalExceptionFactory.GetServiceException( CoreErrorCodes.NonParsableOAuthError, CoreErrorMessages.NonParsableOAuthError, response); } catch (Exception ex) { exceptionToThrow = MsalExceptionFactory.GetServiceException(CoreErrorCodes.UnknownError, response.Body, ex, response); } if (exceptionToThrow == null) { exceptionToThrow = response.StatusCode != HttpStatusCode.NotFound ? MsalExceptionFactory.GetServiceException(CoreErrorCodes.HttpStatusCodeNotOk, httpErrorCodeMessage, response) : MsalExceptionFactory.GetServiceException(CoreErrorCodes.HttpStatusNotFound, httpErrorCodeMessage, response); } if (shouldLogAsError) { requestContext.Logger.ErrorPii(exceptionToThrow); } else { requestContext.Logger.InfoPii(exceptionToThrow); } throw exceptionToThrow; }
internal async Task <T> ExecuteRequestAsync <T>(Uri endPoint, HttpMethod method, RequestContext requestContext) { bool addCorrelationId = requestContext != null && !string.IsNullOrEmpty(requestContext.Logger.CorrelationId.ToString()); if (addCorrelationId) { _headers.Add(OAuth2Header.CorrelationId, requestContext.Logger.CorrelationId.ToString()); _headers.Add(OAuth2Header.RequestCorrelationIdInResponse, "true"); } HttpResponse response = null; var endpointUri = CreateFullEndpointUri(endPoint); var httpEvent = new HttpEvent() { HttpPath = endpointUri, QueryParams = endpointUri.Query }; using (_telemetryManager.CreateTelemetryHelper(requestContext.TelemetryRequestId, requestContext.ClientId, httpEvent)) { if (method == HttpMethod.Post) { response = await _httpManager.SendPostAsync(endpointUri, _headers, _bodyParameters, requestContext) .ConfigureAwait(false); } else { response = await _httpManager.SendGetAsync(endpointUri, _headers, requestContext).ConfigureAwait(false); } httpEvent.HttpResponseStatus = (int)response.StatusCode; httpEvent.UserAgent = response.UserAgent; httpEvent.HttpMethod = method.Method; IDictionary <string, string> headersAsDictionary = response.HeadersAsDictionary; if (headersAsDictionary.ContainsKey("x-ms-request-id") && headersAsDictionary["x-ms-request-id"] != null) { httpEvent.RequestIdHeader = headersAsDictionary["x-ms-request-id"]; } if (headersAsDictionary.ContainsKey("x-ms-clitelem") && headersAsDictionary["x-ms-clitelem"] != null) { XmsCliTelemInfo xmsCliTeleminfo = new XmsCliTelemInfoParser().ParseXMsTelemHeader(headersAsDictionary["x-ms-clitelem"], requestContext); if (xmsCliTeleminfo != null) { httpEvent.TokenAge = xmsCliTeleminfo.TokenAge; httpEvent.SpeInfo = xmsCliTeleminfo.SpeInfo; httpEvent.ServerErrorCode = xmsCliTeleminfo.ServerErrorCode; httpEvent.ServerSubErrorCode = xmsCliTeleminfo.ServerSubErrorCode; } } if (response.StatusCode != HttpStatusCode.OK) { try { httpEvent.OauthErrorCode = JsonHelper.DeserializeFromJson <MsalTokenResponse>(response.Body).Error; } catch (SerializationException) // in the rare case we get an error response we cannot deserialize { throw MsalExceptionFactory.GetServiceException( CoreErrorCodes.NonParsableOAuthError, CoreErrorMessages.NonParsableOAuthError, response); } } } return(CreateResponse <T>(response, requestContext, addCorrelationId)); }
private async Task <HttpResponse> ExecuteWithRetryAsync( Uri endpoint, IDictionary <string, string> headers, HttpContent body, HttpMethod method, RequestContext requestContext, bool doNotThrow = false, bool retry = true) { Exception timeoutException = null; bool isRetryable = false; HttpResponse response = null; try { HttpContent clonedBody = body; if (body != null) { // Since HttpContent would be disposed by underlying client.SendAsync(), // we duplicate it so that we will have a copy in case we would need to retry clonedBody = await CloneHttpContentAsync(body).ConfigureAwait(false); } response = await ExecuteAsync(endpoint, headers, clonedBody, method).ConfigureAwait(false); if (response.StatusCode == HttpStatusCode.OK) { return(response); } requestContext.Logger.Info(string.Format(CultureInfo.InvariantCulture, CoreErrorMessages.HttpRequestUnsuccessful, (int)response.StatusCode, response.StatusCode)); if ((int)response.StatusCode >= 500 && (int)response.StatusCode < 600) { isRetryable = true; } } catch (TaskCanceledException exception) { requestContext.Logger.ErrorPii(exception); isRetryable = true; timeoutException = exception; } if (isRetryable) { if (retry) { requestContext.Logger.Info("Retrying one more time.."); await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false); return(await ExecuteWithRetryAsync( endpoint, headers, body, method, requestContext, doNotThrow, retry : false).ConfigureAwait(false)); } requestContext.Logger.Info("Request retry failed."); if (timeoutException != null) { throw MsalExceptionFactory.GetServiceException( CoreErrorCodes.RequestTimeout, "Request to the endpoint timed out.", timeoutException, null); // no http response to add more details to this exception } if (doNotThrow) { return(response); } throw MsalExceptionFactory.GetServiceException( CoreErrorCodes.ServiceNotAvailable, "Service is unavailable to process the request", response); } return(response); }