/// <summary>
        /// Handles the remote callback asynchronous.
        /// </summary>
        /// <returns></returns>
        protected virtual async Task <bool> HandleRemoteCallbackAsync()
        {
            var ticket = await this.AuthenticateAsync().ConfigureAwait(false);

            if (ticket == null)
            {
                var errorContext = new RemoteFailureContext(
                    this.Context,
                    this.Scheme,
                    this.Options,
                    new Exception("Invalid return state, unable to redirect."));

                await this.Options.Events.RemoteFailure(errorContext).ConfigureAwait(false);

                if (errorContext.Result.Handled)
                {
                    return(true);
                }

                if (errorContext.Result.Skipped)
                {
                    return(false);
                }

                this.Context.Response.StatusCode = 500;
                return(true);
            }


            return(true);
        }
        private static Task HandleOnRemoteFailure(RemoteFailureContext context)
        {
            context.Response.Redirect(context.Properties.RedirectUri);
            context.HandleResponse();

            return(Task.FromResult(0));
        }
        private async Task AddMicrosoftIdentityWebApp_TestB2cSpecificSetup(IServiceCollection services, Func <RemoteFailureContext, Task> remoteFailureFuncMock)
        {
            var provider = services.BuildServiceProvider();

            var oidcOptions = provider.GetRequiredService <IOptionsFactory <OpenIdConnectOptions> >().Create(OidcScheme);

            // Assert B2C name claim type
            Assert.Equal(ClaimConstants.Name, oidcOptions.TokenValidationParameters.NameClaimType);

            var(httpContext, authScheme, authProperties)  = CreateContextParameters(provider);
            authProperties.Items[OidcConstants.PolicyKey] = TestConstants.B2CEditProfileUserFlow;

            var redirectContext = new RedirectContext(httpContext, authScheme, oidcOptions, authProperties)
            {
                ProtocolMessage = new OpenIdConnectMessage()
                {
                    IssuerAddress = $"IssuerAddress/{TestConstants.B2CSignUpSignInUserFlow}/"
                },
            };

            (httpContext, authScheme, authProperties) = CreateContextParameters(provider);

            var remoteFailureContext = new RemoteFailureContext(httpContext, authScheme, new RemoteAuthenticationOptions(), new Exception());

            await oidcOptions.Events.RedirectToIdentityProvider(redirectContext).ConfigureAwait(false);

            await oidcOptions.Events.RemoteFailure(remoteFailureContext).ConfigureAwait(false);

            await remoteFailureFuncMock.ReceivedWithAnyArgs().Invoke(Arg.Any <RemoteFailureContext>()).ConfigureAwait(false);

            // Assert issuer is updated to non-default user flow
            Assert.Contains(TestConstants.B2CEditProfileUserFlow, redirectContext.ProtocolMessage.IssuerAddress);
            Assert.NotNull(redirectContext.ProtocolMessage.Parameters[ClaimConstants.ClientInfo]);
            Assert.Equal(Constants.One, redirectContext.ProtocolMessage.Parameters[ClaimConstants.ClientInfo].ToString(CultureInfo.InvariantCulture));
        }
Esempio n. 4
0
 private static Task HandleRemoteLoginFailure(RemoteFailureContext ctx)
 {
     ctx.HttpContext.Items["ErrorMessage"] = ctx.Failure.Message;
     ctx.Response.Redirect("/Account/Login");
     ctx.HandleResponse();
     return(Task.CompletedTask);
 }
 /// <summary>
 /// Returns whether the specified failure context indicates that request correlation for XSRF failed.
 /// </summary>
 /// <param name="context">The current failure context.</param>
 /// <returns>
 /// <see langword="true"/> if request correlation failed; otherwise <see langword="false"/>.
 /// </returns>
 private static bool IsCorrelationFailure(RemoteFailureContext context)
 {
     // See https://github.com/aspnet/Security/blob/ad425163b29b1e09a41e84423b0dcbac797c9164/src/Microsoft.AspNetCore.Authentication.OAuth/OAuthHandler.cs#L66
     // and https://github.com/aspnet/Security/blob/2d1c56ce5ccfc15c78dd49cee772f6be473f3ee2/src/Microsoft.AspNetCore.Authentication/RemoteAuthenticationHandler.cs#L203
     // This effectively means that the user did not pass their cookies along correctly to correlate the request.
     return(string.Equals(context.Failure.Message, "Correlation failed.", StringComparison.Ordinal));
 }
Esempio n. 6
0
        public static async Task Handle_Denied_Permissions(string query, string expected)
        {
            // Arrange
            var httpContext = new DefaultHttpContext();

            var request = new DefaultHttpRequest(httpContext)
            {
                QueryString = new QueryString(query)
            };

            var scheme  = new AuthenticationScheme("amazon-auth", "Amazon", typeof(AmazonAuthenticationHandler));
            var options = new RemoteAuthenticationOptions();
            var failure = new InvalidOperationException();

            var context          = new RemoteFailureContext(httpContext, scheme, options, failure);
            var provider         = Guid.NewGuid().ToString();
            var secureDataFormat = Mock.Of <ISecureDataFormat <object> >();
            var logger           = Mock.Of <ILogger>();

            // Act
            await OAuthEventsHandler.HandleRemoteFailure(context, provider, secureDataFormat, logger, PropertiesProvider);

            // Assert
            httpContext.Response.GetTypedHeaders().Location.OriginalString.ShouldBe(expected);
        }
Esempio n. 7
0
        public async Task OnRemoteError_HandlesResponseWhenUserCancelsFlowFromTheAzureADB2CUserInterface()
        {
            // Arrange

            var handlers = new AzureADB2COpenIDConnectEventHandlers(
                AzureADB2CDefaults.AuthenticationScheme,
                new AzureADB2COptions()
            {
                SignUpSignInPolicyId = "B2C_1_SiUpIn"
            });

            var remoteFailureContext = new RemoteFailureContext(
                new DefaultHttpContext(),
                new AuthenticationScheme(
                    AzureADB2CDefaults.AuthenticationScheme,
                    displayName: null,
                    handlerType: typeof(OpenIdConnectHandler)),
                new OpenIdConnectOptions(),
                new OpenIdConnectProtocolException("access_denied"));

            // Act
            await handlers.OnRemoteFailure(remoteFailureContext);

            // Assert
            Assert.Equal(StatusCodes.Status302Found, remoteFailureContext.Response.StatusCode);
            Assert.Equal("/", remoteFailureContext.Response.Headers.Location);
            Assert.True(remoteFailureContext.Result.Handled);
        }
Esempio n. 8
0
        public async Task OnRemoteError_HandlesResponseWhenTryingToResetPasswordFromTheLoginPage()
        {
            // Arrange

            var handlers = new AzureADB2COpenIDConnectEventHandlers(
                AzureADB2CDefaults.AuthenticationScheme,
                new AzureADB2COptions()
            {
                SignUpSignInPolicyId = "B2C_1_SiUpIn"
            });

            var remoteFailureContext = new RemoteFailureContext(
                new DefaultHttpContext(),
                new AuthenticationScheme(
                    AzureADB2CDefaults.AuthenticationScheme,
                    displayName: null,
                    handlerType: typeof(OpenIdConnectHandler)),
                new OpenIdConnectOptions(),
                new OpenIdConnectProtocolException("AADB2C90118"));

            // Act
            await handlers.OnRemoteFailure(remoteFailureContext);

            // Assert
            Assert.Equal(StatusCodes.Status302Found, remoteFailureContext.Response.StatusCode);
            Assert.Equal("/AzureADB2C/Account/ResetPassword/AzureADB2C", remoteFailureContext.Response.Headers.Location);
            Assert.True(remoteFailureContext.Result.Handled);
        }
Esempio n. 9
0
        public async Task OnRemoteError_HandlesResponseWhenErrorIsUnknown()
        {
            // Arrange

            var handlers = new AzureADB2COpenIDConnectEventHandlers(
                AzureADB2CDefaults.AuthenticationScheme,
                new AzureADB2COptions()
            {
                SignUpSignInPolicyId = "B2C_1_SiUpIn"
            });

            var remoteFailureContext = new RemoteFailureContext(
                new DefaultHttpContext(),
                new AuthenticationScheme(
                    AzureADB2CDefaults.AuthenticationScheme,
                    displayName: null,
                    handlerType: typeof(OpenIdConnectHandler)),
                new OpenIdConnectOptions(),
                new OpenIdConnectProtocolException("some_other_error"));

            // Act
            await handlers.OnRemoteFailure(remoteFailureContext);

            // Assert
            Assert.Equal(StatusCodes.Status302Found, remoteFailureContext.Response.StatusCode);
            Assert.Equal("/AzureADB2C/Account/Error", remoteFailureContext.Response.Headers[HeaderNames.Location]);
            Assert.True(remoteFailureContext.Result.Handled);
        }
Esempio n. 10
0
        public static async Task HandleOnRemoteFailure(RemoteFailureContext context)
        {
            context.Response.StatusCode  = 500;
            context.Response.ContentType = "text/html";
            await context.Response.WriteAsync("<html><body>");

            await context.Response.WriteAsync("A remote failure has occurred: " + UrlEncoder.Default.Encode(context.Failure.Message) + "<br>");

            /* unkown issue
             * if (context.Properties != null)
             * {
             *  await context.Response.WriteAsync("Properties:<br>");
             *  foreach (var pair in context.Properties.Items)
             *  {
             *      await context.Response.WriteAsync($"-{ UrlEncoder.Default.Encode(pair.Key)}={ UrlEncoder.Default.Encode(pair.Value)}<br>");
             *  }
             * }*/

            await context.Response.WriteAsync("<a href=\"/\">Home</a>");

            await context.Response.WriteAsync("</body></html>");

            // context.Response.Redirect("/error?FailureMessage=" + UrlEncoder.Default.Encode(context.Failure.Message));

            context.HandleResponse();
        }
Esempio n. 11
0
        private async Task HandleOnRemoteFailure(RemoteFailureContext context)
        {
            context.Response.StatusCode  = 500;
            context.Response.ContentType = "text/html";
            await context.Response.WriteAsync("<html><head><link rel='preconnect' href='https://fonts.gstatic.com'><link href='https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap' rel ='stylesheet'><link rel='stylesheet' href='css/bootstrap.min.css'/><link rel='stylesheet' href='css/style.css'/></head><body><div class='login-wrapper'><div class='container'><div class='logo-wrapper'><img class='logo-img' src='image/logo.png' alt='' /></div>");

            await context.Response.WriteAsync("<div><div class='login-form mb-4' style=height:50% !important;><h4>You have denied the application permissions.<br> Please try again.</h4><br>");

            //await context.Response.WriteAsync("A remote failure has occurred: <br>" +
            //    context.Failure.Message.Split(Environment.NewLine).Select(s => HtmlEncoder.Default.Encode(s) + "<br>").Aggregate((s1, s2) => s1 + s2));

            //if (context.Properties != null)
            //{
            //    await context.Response.WriteAsync("Properties:<br>");
            //    foreach (var pair in context.Properties.Items)
            //    {
            //        await context.Response.WriteAsync($"-{ HtmlEncoder.Default.Encode(pair.Key)}={ HtmlEncoder.Default.Encode(pair.Value)}<br>");
            //    }
            //}

            await context.Response.WriteAsync("<h5><a href=\"/\">Home</a></h5></div></div></div>");

            await context.Response.WriteAsync("</body></html>");

            // context.Response.Redirect("/error?FailureMessage=" + UrlEncoder.Default.Encode(context.Failure.Message));

            context.HandleResponse();
        }
Esempio n. 12
0
        /// <summary>
        /// Get Request Token
        /// </summary>
        /// <returns></returns>
        private async Task <PocketRequestTokenResult> GetRequestTokenAsync()
        {
            try
            {
                var requestBody = new PocketRequestTokenRequest()
                {
                    ConsumerKey = Options.ConsumerKey, RedirectUri = BuildRedirectUri(Options.CallbackPath.Value)
                };
                var playLoad = PrepareRequestTokenPlayLoad(requestBody);
                var res      = await Options.Backchannel.PostAsync(Options.RequestTokenEndpoint, playLoad);

                if (res.IsSuccessStatusCode)
                {
                    var str = await res.Content.ReadAsStringAsync();

                    var tokenRes = JsonConvert.DeserializeObject <PocketRequestTokenResponse>(str);
                    return(PocketRequestTokenResult.Success(tokenRes));
                }
                else
                {
                    await ProcessPocketRemoteFail(res);
                }
            }
            catch (Exception ex)
            {
                var remoteFailure = new RemoteFailureContext(Context, Scheme, Options, ex);
                await Events.OnRemoteFailure(remoteFailure);
            }
            return(PocketRequestTokenResult.Fail());
        }
Esempio n. 13
0
        private async Task HandleOnRemoteFailure(RemoteFailureContext context)
        {
            context.Response.StatusCode  = 500;
            context.Response.ContentType = "text/html";
            await context.Response.WriteAsync("<html><body>");

            await context.Response.WriteAsync("A remote failure has occurred: <br>" +
                                              context.Failure.Message.Split(Environment.NewLine).Select(s => HtmlEncoder.Default.Encode(s) + "<br>").Aggregate((s1, s2) => s1 + s2));

            if (context.Properties != null)
            {
                await context.Response.WriteAsync("Properties:<br>");

                foreach (var pair in context.Properties.Items)
                {
                    await context.Response.WriteAsync($"-{ HtmlEncoder.Default.Encode(pair.Key)}={ HtmlEncoder.Default.Encode(pair.Value)}<br>");
                }
            }

            await context.Response.WriteAsync("<a href=\"/\">Home</a>");

            await context.Response.WriteAsync("</body></html>");

            // context.Response.Redirect("/error?FailureMessage=" + UrlEncoder.Default.Encode(context.Failure.Message));

            context.HandleResponse();
        }
Esempio n. 14
0
        public Task OnRemoteFailure(RemoteFailureContext context)
        {
            context.HandleResponse();
            // Handle the error code that Azure Active Directory B2C throws when trying to reset a password from the login page
            // because password reset is not supported by a "sign-up or sign-in policy".
            // Below is a sample error message:
            // 'access_denied', error_description: 'AADB2C90118: The user has forgotten their password.
            // Correlation ID: f99deff4-f43b-43cc-b4e7-36141dbaf0a0
            // Timestamp: 2018-03-05 02:49:35Z
            //', error_uri: 'error_uri is null'.
            if (context.Failure is OpenIdConnectProtocolException && context.Failure.Message.Contains("AADB2C90118"))
            {
                // If the user clicked the reset password link, redirect to the reset password route
                context.Response.Redirect($"{context.Request.PathBase}/AzureADB2C/Account/ResetPassword/{SchemeName}");
            }
            // Access denied errors happen when a user cancels an action on the Azure Active Directory B2C UI. We just redirect back to
            // the main page in that case.
            // Message contains error: 'access_denied', error_description: 'AADB2C90091: The user has cancelled entering self-asserted information.
            // Correlation ID: d01c8878-0732-4eb2-beb8-da82a57432e0
            // Timestamp: 2018-03-05 02:56:49Z
            // ', error_uri: 'error_uri is null'.
            else if (context.Failure is OpenIdConnectProtocolException && context.Failure.Message.Contains("access_denied"))
            {
                context.Response.Redirect($"{context.Request.PathBase}/");
            }
            else
            {
                context.Response.Redirect($"{context.Request.PathBase}/AzureADB2C/Account/Error");
            }

            return(Task.CompletedTask);
        }
        /// <summary>
        /// Returns whether the specified failure context indicates the user denied account linking permission.
        /// </summary>
        /// <param name="context">The current failure context.</param>
        /// <returns>
        /// <see langword="true"/> if account linking permission was denied; otherwise <see langword="false"/>.
        /// </returns>
        private static bool WasPermissionDenied(RemoteFailureContext context)
        {
            string error = context.Request.Query["error"].FirstOrDefault();

            if (string.Equals(error, "access_denied", StringComparison.Ordinal) ||
                string.Equals(error, "consent_required", StringComparison.Ordinal))
            {
                return(true);
            }

            string reason = context.Request.Query["error_reason"].FirstOrDefault();

            if (string.Equals(reason, "user_denied", StringComparison.Ordinal))
            {
                return(true);
            }

            string description = context.Request.Query["error_description"].FirstOrDefault();

            if (!string.IsNullOrEmpty(description) &&
                description.Contains("denied", StringComparison.OrdinalIgnoreCase))
            {
                return(true);
            }

            return(context.Request.Query.ContainsKey("denied"));
        }
Esempio n. 16
0
        public Task OnRemoteFailure(RemoteFailureContext context)
        {
            context.HandleResponse();
            // Handle the error code that Azure AD B2C throws when trying to reset a password from the login page
            // because password reset is not supported by a "sign-up or sign-in policy"
            if (context.Failure is OpenIdConnectProtocolException && context.Failure.Message.Contains("AADB2C90118"))
            {
                context.Response.Redirect("/Session/ResetPassword");
            }
            else if (context.Failure is OpenIdConnectProtocolException &&
                     context.Failure.Message.Contains("access_denied"))
            {
                context.Response.Redirect("/");
            }
            else
            {
                // https://github.com/Azure-Samples/active-directory-b2c-dotnetcore-webapp/issues/29
                var message = Regex.Replace(context.Failure.Message, @"[^\u001F-\u007F]+", string.Empty);
                context.Response.Redirect("/Home/Error?message=" + message);
                // context.Response.Redirect("/Home/Error?message=" + context.Failure.Message);

                /* if you have this exception:
                 * Message contains error: 'invalid_request', error_description: 'AADB2C90205: This application does not have sufficient permissions against this web resource to perform the operation.
                 * Correlation ID: 073af821-4d5c-4db1-9d51-5f57d2c148e2Timestamp: 2018-04-09 09:37:13Z', error_uri: 'error_uri is null'.
                 *
                 * Please check this https://github.com/Azure-Samples/active-directory-b2c-javascript-msal-singlepageapp/issues/4
                 */
            }

            return(Task.FromResult(0));
        }
Esempio n. 17
0
 private async Task HandleOnRemoteFailure(RemoteFailureContext context)
 {
     if (context.Failure.Message.Contains("access_denied"))
     {
         context.Response.StatusCode = 403;
     }
     context.HandleResponse();
 }
Esempio n. 18
0
        // Handle sign-in errors differently than generic errors.
        private Task OnAuthenticationFailed(RemoteFailureContext context)
        {
            context.HandleResponse();
            var message = Regex.Replace(context.Failure?.Message, @"[^\u001F-\u007F]+", string.Empty);

            context.Response.Redirect("/Home/Error?message=" + message);
            return(Task.FromResult(0));
        }
            /// <summary>
            /// Method to handle the remote failures if any from authentication server
            /// </summary>
            /// <param name="context"> The RemoteFailureContext that contains the failure error message </param>
            /// <returns> Throw the exception with the received failure message from remote </returns>
            public Task OnRemoteFailure(RemoteFailureContext context)
            {
                context.HandleResponse();

                // Throw the exception to log the failure message in ApplicationInsights AND respond user with system failure message
                // This is further handled by ExceptionAttribute class
                throw (new Exception(context.Failure.Message));
            }
Esempio n. 20
0
        public Task HandleRemoteFailure(RemoteFailureContext context)
        {
            this.logger.LogError(EventIDs.ExternalAuthNProviderError, context.Failure, LogMessages.AuthNProviderError);
            context.HandleResponse();
            context.Response.Redirect($"/Home/AuthNError?messageid={(int)AuthNFailureMessageID.ExternalAuthNProviderError}");

            return(Task.CompletedTask);
        }
Esempio n. 21
0
        public override Task RemoteFailure(RemoteFailureContext context)
        {
            _log.WriteErrorAsync("Authentication", "RemoteFailure", context.Failure.Message + context.Failure.InnerException, context.Failure).Wait();

            context.HandleResponse();
            context.Response.Redirect("/Home/AuthenticationFailed");

            return(Task.FromResult(0));
        }
Esempio n. 22
0
        // TODO: Replace with better implementation, use built-in error handling.
        /// <summary>
        /// When an oath authorization or token request fails.
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public static async Task HandleOnRemoteFailure(RemoteFailureContext context)
        {
            var handler = context.HttpContext.RequestServices.GetRequiredService <JsonErrorHandler>();

            context.Response.StatusCode  = (int)HttpStatusCode.InternalServerError;
            context.Response.ContentType = "application/json";
            await context.Response.WriteAsync(handler.Serialize(new OauthException(context.Failure)));

            context.HandleResponse();
        }
Esempio n. 23
0
        private async Task HandleOnRemoteFailure(RemoteFailureContext context)
        {
            var url = context.Request.Host.ToString();

            context.HandleResponse();
            await Task.Run(() =>
            {
                context.Response.Redirect("/Home", true);
            });
        }
        /// <summary>
        /// 远程服务器(如授权失败时)错误处理程序。
        /// </summary>
        /// <param name="remoteFailureContext"></param>
        /// <returns></returns>
        private Task OnRemoteFailureHandler(RemoteFailureContext remoteFailureContext)
        {
            remoteFailureContext.HandleResponse();

            if (!remoteFailureContext.HttpContext.Response.HasStarted)
            {
                //TODO 写入日志
                //TODO 向HttpContext.Response写入友好的错误提示信息展示给用户
            }
            return(Task.CompletedTask);
        }
        public static Task HandleRemoteFailure(this RemoteFailureContext context)
        {
            Log.Error(
                context.Failure,
                "External authentication remote failure. {Scheme}",
                context.Scheme.Name);

            context.Response.RedirectExternalError(context.Scheme.Name, context.Properties);
            context.HandleResponse();

            return(Task.CompletedTask);
        }
Esempio n. 26
0
 private Task HandleRemoteFailure(RemoteFailureContext context)
 {
     if (context.Failure.Message.ToLower().Contains("correlation"))
     {
         context.HandleResponse();
         context.Response.Redirect("/Home/CorrelationError");
         return(Task.FromResult(0));
     }
     else
     {
         throw context.Failure;
     }
 }
Esempio n. 27
0
#pragma warning disable CS1998 
        private async Task HandleOnRemoteFailure(RemoteFailureContext context)
        {
            var msg = context.Failure.Message.Split(Environment.NewLine).Select(s => s + Environment.NewLine).Aggregate((s1, s2) => s1 + s2);

            if (context.Properties != null)
                foreach (var pair in context.Properties.Items)
                    msg = $"{msg}{Environment.NewLine}-{pair.Key}={pair.Value}";

            Log.Logger.Error($"External authentication error: {msg}");

            context.Response.Redirect($"/externalauth/error/{ErrorEnum.ExternalAuthError}");

            context.HandleResponse();
        }
 /// <summary>
 /// Handles a remote failure.
 /// </summary>
 /// <param name="context">The failure context.</param>
 /// <returns>
 /// A <see cref="Task"/> representing the completion of the operation.
 /// </returns>
 private async Task HandleRemoteFailure(RemoteFailureContext context)
 {
     try
     {
         await HandleRemoteFailure(
             context,
             _options.SignInScheme,
             _options.StateDataFormat,
             _logger,
             (p) => p?.Items);
     }
     catch (Exception ex)
     {
         _logger.LogError(default, ex, "Failed to handle remote failure: {Message}.", ex.Message);
Esempio n. 29
0
        /// <summary>
        /// Handles a remote failure.
        /// </summary>
        /// <param name="context">The failure context.</param>
        /// <returns>
        /// A <see cref="Task"/> representing the completion of the operation.
        /// </returns>
        private async Task HandleRemoteFailure(RemoteFailureContext context)
        {
            try
            {
                await HandleRemoteFailure(
                    context,
                    _options.SignInScheme !,
                    _options.StateDataFormat,
                    _logger,
                    (p) => p?.Items);
            }
#pragma warning disable CA1031
            catch (Exception ex)
#pragma warning restore CA1031
            {
                _logger.LogError(default, ex, "Failed to handle remote failure: {Message}.", ex.Message);
        /// <summary>
        /// Handles a remote failure.
        /// </summary>
        /// <typeparam name="T">The type of the secure data.</typeparam>
        /// <param name="context">The failure context.</param>
        /// <param name="provider">The authentication provider.</param>
        /// <param name="secureDataFormat">The secure data format.</param>
        /// <param name="logger">The <see cref="ILogger"/> to use.</param>
        /// <param name="propertiesProvider">A delegate to a method to retrieve authentication properties from the secure data.</param>
        /// <returns>
        /// A <see cref="Task"/> representing the completion of the operation.
        /// </returns>
        public static Task HandleRemoteFailure <T>(
            RemoteFailureContext context,
            string provider,
            ISecureDataFormat <T> secureDataFormat,
            ILogger logger,
            Func <T, IDictionary <string, string>?> propertiesProvider)
        {
            string?path = GetSiteErrorRedirect(context, secureDataFormat, propertiesProvider);

            if (string.IsNullOrEmpty(path) ||
                !Uri.TryCreate(path, UriKind.Relative, out Uri? notUsed))
            {
                path = "/";
            }

            SiteMessage message;

            if (WasPermissionDenied(context))
            {
                message = SiteMessage.LinkDenied;
                logger.LogTrace("User denied permission.");
            }
            else
            {
                message = SiteMessage.LinkFailed;

                var    eventId    = default(EventId);
                string errors     = string.Join(";", context.Request.Query.Select((p) => $"'{p.Key}' = '{p.Value}'"));
                string logMessage = $"Failed to sign-in using '{provider}': '{context.Failure.Message}'. Errors: {errors}.";

                if (IsCorrelationFailure(context))
                {
                    // Not a server-side problem, so do not create log noise
                    logger.LogTrace(eventId, context.Failure, logMessage);
                }
                else
                {
                    logger.LogError(eventId, context.Failure, logMessage);
                }
            }

            context.Response.Redirect($"{path}?Message={message}");
            context.HandleResponse();

            return(Task.CompletedTask);
        }