示例#1
0
        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);
        }
示例#3
0
        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());
        }
示例#4
0
        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);
            }
        }
示例#6
0
        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;
        }
示例#7
0
        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));
        }
示例#8
0
        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);
        }
示例#9
0
        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;
        }
示例#10
0
        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);
        }