New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support automatic redirect on remote failures #1165

Closed
JeanCollas opened this Issue Mar 26, 2017 · 25 comments

Comments

Projects
None yet
8 participants
@JeanCollas
Contributor

JeanCollas commented Mar 26, 2017

From time to time when people try to log in using Facebook, I get this error, what does it mean exactly? How to handle it?

Unhandled remote failure. (Correlation failed.) 
Microsoft.AspNetCore.Authentication 
Correlation failed. 

-------- 
-------- 
-------- Exception -------- 
System.AggregateException: Unhandled remote failure. (Correlation failed.) ---> System.Exception: Correlation failed. 
--- End of inner exception stack trace --- 
at Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler'1.d__5.MoveNext() 
--- End of stack trace from previous location where exception was thrown --- 
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) 
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) 
at Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler'1.d__4.MoveNext() 
--- End of stack trace from previous location where exception was thrown --- 
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) 
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) 
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware'1.d__18.MoveNext() 
--- End of stack trace from previous location where exception was thrown --- 
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware'1.d__18.MoveNext() 
--- End of stack trace from previous location where exception was thrown --- 
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) 
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) 
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware'1.d__18.MoveNext() 
--- End of stack trace from previous location where exception was thrown --- 
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware'1.d__18.MoveNext() 
--- End of stack trace from previous location where exception was thrown --- 
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) 
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) 
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware'1.d__18.MoveNext() 
--- End of stack trace from previous location where exception was thrown --- 
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware'1.d__18.MoveNext() 
--- End of stack trace from previous location where exception was thrown --- 
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) 
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) 
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware'1.d__18.MoveNext() 
--- End of stack trace from previous location where exception was thrown --- 
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware'1.d__18.MoveNext() 
--- End of stack trace from previous location where exception was thrown --- 
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) 
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) 
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware'1.d__18.MoveNext() 
--- End of stack trace from previous location where exception was thrown --- 
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware'1.d__18.MoveNext() 
--- End of stack trace from previous location where exception was thrown --- 
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) 
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) 
at Microsoft.ApplicationInsights.AspNetCore.ExceptionTrackingMiddleware.d__4.MoveNext() 
--- End of stack trace from previous location where exception was thrown --- 
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) 
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) 
at Microsoft.AspNetCore.Localization.RequestLocalizationMiddleware.d__4.MoveNext() 
--- End of stack trace from previous location where exception was thrown --- 
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) 
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) 
@Tratcher

This comment has been minimized.

Member

Tratcher commented Mar 27, 2017

There's a correlation cookie issued with each challenge to limit XRSF issues. Under some conditions this cookie gets lost before the authentication is complete. If it does the best thing that you can do is restart the auth flow. You can handle this error by hooking the RemoteFailure event.

OnRemoteFailure = ctx =>
{
ctx.Response.Redirect("/error?FailureMessage=" + UrlEncoder.Default.Encode(ctx.Failure.Message));
ctx.HandleResponse();
return Task.FromResult(0);
}

@JeanCollas

This comment has been minimized.

Contributor

JeanCollas commented Mar 27, 2017

Thanks @Tratcher, as it happens quite regularly (more than 10% of registrations), I feel like I must address the issue.
Restarting the auth flow from there does not risk to get into an infinite loop?

@nhwilly

This comment has been minimized.

nhwilly commented Mar 27, 2017

It seem this - or something related - is happening to me. FB seemed to be working fine. Now it's not.

`Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware:Error: An unhandled exception has occurred while executing the request

System.AggregateException: Unhandled remote failure. (Message contains error: 'server_error', error_description: 'AADB2C: An exception has occured.
Correlation ID: 6f040040-088a-451b-8eff-73f9578316b6
Timestamp: 2017-03-27 20:35:08Z
', error_uri: 'error_uri is null'.) ---> Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectProtocolException: Message contains error: 'server_error', error_description: 'AADB2C: An exception has occured.
Correlation ID: 6f040040-088a-451b-8eff-73f9578316b6
Timestamp: 2017-03-27 20:35:08Z
', error_uri: 'error_uri is null'.
   --- End of inner exception stack trace ---`

Works great for a Microsoft Account. I'm using Azure B2C, btw.

Tried pretty much everything. Any ideas?

@Tratcher

This comment has been minimized.

Member

Tratcher commented Mar 27, 2017

It does risk an infinite loop, so it should include an error page that says "Something went wrong trying to log you in, click here to try again."

@nhwilly

This comment has been minimized.

nhwilly commented Mar 27, 2017

I just tried running this is the policy section of the B2C portal.

Got this:

AggregateException: Unhandled remote failure. (OpenIdConnectAuthenticationHandler: message.State is null or empty.)

Does that help any?

@JeanCollas

This comment has been minimized.

Contributor

JeanCollas commented Mar 28, 2017

Thanks @Tratcher for the confirmation, I will try to handle it in the OnRemoteFailure as suggested.

The Facebook login is a bit tricky when not everything goes well, for example if I:

  • try to log-in via Facebook,
  • accept everything on Facebook,
  • arrive on the confirmation where a password is required to create the account
  • go back to the login page (so do not create the account)
  • try again to log-in via Facebook
    I get redirected to AccessDenied with return url on ExternalLoginCallback. I don't understand why as (I think) it should just be redirected to ExternalLoginCallback.
@martincostello

This comment has been minimized.

martincostello commented Apr 2, 2017

I've just had an instance of this error pop-up in my server logs, and there's this SO Q/A which mentions it being something to do with clock-sync.

@Tratcher

This comment has been minimized.

Member

Tratcher commented Apr 4, 2017

@JeanCollas If you've logged in with the external provider but not completed the local login then you still have the External cookie. If you then restart the auth flow the facebook auth provider will see that you're already signed in and trigger AccessDenied to avoid an infinite login loop. If you want to restart the login process you need to clear the state from the prior attempt, including the external cookie. See https://github.com/aspnet/Templates/blob/c91664a01f6b429b9ff3ac17cacd906037f11b19/src/Rules/StarterWeb/IndividualAuth/Controllers/AccountController.cs#L51

@Tratcher

This comment has been minimized.

Member

Tratcher commented Apr 4, 2017

@nhwilly are you still having Facebook B2C issues? There was an outage on Monday due to a Facebook API change, but that should have been resolved later that day.

@Eilon

This comment has been minimized.

Member

Eilon commented Apr 6, 2017

We should consider the following:

  1. Add an error redirect URL to the Remote Auth middleware
  2. Update the project templates to use this feature and redirect to a page w/ some explanation about how to resolve the issue (because it is generally ultimately a problem on the site visitor's machine, not a server issue)

cc @DamianEdwards for item (2).

@NikasZalias

This comment has been minimized.

NikasZalias commented May 18, 2017

I am getting this error:

System.AggregateException: Unhandled remote failure. (Code was not found.) ---> System.Exception: Code was not found.
   --- End of inner exception stack trace ---
   at Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler`1.<HandleRemoteCallbackAsync>d__6.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler`1.<HandleRequestAsync>d__5.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware`1.<Invoke>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware`1.<Invoke>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware`1.<Invoke>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware`1.<Invoke>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware`1.<Invoke>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware`1.<Invoke>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware`1.<Invoke>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware`1.<Invoke>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware`1.<Invoke>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware`1.<Invoke>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at Microsoft.AspNetCore.Cors.Infrastructure.CorsMiddleware.<Invoke>d__7.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at Microsoft.AspNetCore.Server.IISIntegration.IISMiddleware.<Invoke>d__8.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at Microsoft.AspNetCore.Hosting.Internal.RequestServicesContainerMiddleware.<Invoke>d__3.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd(Task task)
   at Microsoft.AspNetCore.Server.Kestrel.Internal.Http.Frame`1.<RequestProcessingAsync>d__2.MoveNext()
---> (Inner Exception #0) System.Exception: Code was not found.<---

Does anyone know how to fix this?? This problem occurs when I try to log in to application with facebook account and in permissions I press cancel.

@martincostello

This comment has been minimized.

martincostello commented May 18, 2017

@NikasZalias The middleware doesn't seem to nicely handle permission being denied by the end user, so you end up with an error page. I've worked around this by wrapping the OAuthEvents and adding a custom handler for OnRemoteFailure, which then does a custom redirect if the user denied permissions.

For example:

/// <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(FailureContext context)
{
    return
        string.Equals(context.Request.Query["error"].FirstOrDefault(), "access_denied", StringComparison.Ordinal) ||
        string.Equals(context.Request.Query["error_reason"].FirstOrDefault(), "user_denied", StringComparison.Ordinal) ||
        context.Request.Query.ContainsKey("denied");
}

There might be more conditions that indicate this, but these three all handled this happening in the social login providers I use.

@NikasZalias

This comment has been minimized.

NikasZalias commented May 19, 2017

@martincostello how to call this method in controller? because when I go to ExternalLogin action which looks like this:

[HttpGet]
[AllowAnonymous]
public IActionResult ExternalLogin(string provider, string returnUrl = null)
{
          var redirectUrl = Url.Action("ExternalLoginCallback", "Account", new {ReturnUrl = returnUrl});
          var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl);
           return new ChallengeResult(provider, properties);
 }

It redirects me to facebook permission page, and after I click cancel I get the error I wrote before and I don't know how to handle this callback..

@martincostello

This comment has been minimized.

martincostello commented May 19, 2017

@NikasZalias You need to hook up a handler for RemoteFailure to the Events property of an instance of FacebookOptions when registering Facebook for use, so you'll need to use one of the overloads of UseFacebookAuthentication() that takes an instance of FacebookOptions as a parameter.

@Eilon Eilon added the task label Jun 15, 2017

@Eilon Eilon added this to the 2.1.0 milestone Jun 15, 2017

@Eilon

This comment has been minimized.

Member

Eilon commented Jun 15, 2017

Handling the RemoteFailure event is the right thing to do. We should update our docs/samples to show how to handle that event and at least show a more appropriate message to the user. The error page sample could include a link to enabled the user to try logging in again.

Unfortunately it's difficult to implement this event in a very generic way that's also super useful because each remote auth provider has its own behavior for different types of failures.

@Eilon

This comment has been minimized.

Member

Eilon commented Aug 10, 2017

Need to find out for each external auth provider what behavior they have when auth is cancelled. We should then handle those cases and add a feature that would redirect to a well-known (but configurable) location, which we'd have to add to the templates. E.g. /Account/ExternalLoginCancelled.

@Eilon

This comment has been minimized.

Member

Eilon commented Aug 10, 2017

Need to also see if "denied" is the same as "cancel" for the various providers.

@Tratcher

This comment has been minimized.

Member

Tratcher commented Jan 2, 2018

[Clearing the milestone for re-triage]

I've investigated adding an error redirect but found that it creates an overall worse experience than the current templates. (See #1582 (comment)).

We should instead document the custom error handling process.

@Tratcher

This comment has been minimized.

Member

Tratcher commented Jan 3, 2018

@PinpointTownes noticed this is a duplicate of #710

@Tratcher Tratcher removed the 2 - Working label Jan 9, 2018

@Eilon Eilon added this to the 2.2.0 milestone Jan 26, 2018

@JeanCollas

This comment has been minimized.

Contributor

JeanCollas commented Feb 15, 2018

I am still seeing this error: Correlation failed.
At first I thought it was linked to the OS of the user, but the last few user-agent I got in the past hours seem to be from many platform:

Mozilla/5.0 (Linux; Android 7.0; SAMSUNG SM-G935F Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/6.2 Chrome/56.0.2924.87 Mobile Safari/537.36 
Mozilla/5.0 (Linux; Android 6.0.1; Redmi 3S Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.137 Mobile Safari/537.36 
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36 
Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36 
Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_3 like Mac OS X) AppleWebKit/603.3.8 (KHTML, like Gecko) Version/10.0 Mobile/14G60 Safari/602.1 
Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.167 Safari/537.36 
Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.167 Safari/537.36 
Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.167 Safari/537.36 
Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.137 Mobile Safari/537.36 

Therefore I don't see any pattern in them.

As it does not happen every time, but very often, I have no idea on how to solve this.

@evgrud

This comment has been minimized.

evgrud commented Feb 22, 2018

@JeanCollas In my case it would only happen with Facebook login when on mobile. For certain cases the window with the redirect to Facebook remained open. I'm assuming (also tested on my own device) it happened because Facebook was installed as a Web App which caused the redirect from Facebook to my app after giving my app authorization to be opened in a new window.

The redirect back to my app (in the new window) would finish the login successfully and clear the Correlation cookie. If later the user opened the window with the old Facebook authorization it will redirect him again in a new window to my app but this time the login would fail because there's no longer a Correlation cookie and the Correlation cookie not found warning would be logged.

Obviously I don't consider this a bug of ASP.NET.

@JeanCollas

This comment has been minimized.

Contributor

JeanCollas commented Feb 23, 2018

@evgrud I identified one behavior which always leads to this issue:
When arriving back from the facebook login, I have a set up a last screen for the user to add a password before validating his account. In the case the user hit the "back" button, it always gives this error.

@skyflyer

This comment has been minimized.

skyflyer commented May 11, 2018

Issue #710 was closed in favour of this one, though this one is not specifically related to "access_denied" errors. I second the need for an "easy" access_denied handling as opposed to other scenarios. I'd say access_denied is not an "exception" but rather an anticipated result from an OpenID Connect provider.

Looking through code, I still (?) can't find a way to specify where should the request go in the case of "access denied" errors. What am I missing here?

I figured OnAuthenticationFailed would be called, but it is not. The OnRemoteFailure is called and I can manually redirect there, though it is very cumbersome and to implement it properly, I'd need to check specifically for access_denied status. Given the feedback and the status of #1582 I suppose this will be (perhaps) implemented in 2.2?

@Eilon

This comment has been minimized.

Member

Eilon commented Sep 6, 2018

Would be nice to use Exception.AdditionalData to have some additional key in there that has our dictionary (or data structure) or stuff that the remote provider sent us back as part of the flow.

@Eilon

This comment has been minimized.

Member

Eilon commented Sep 6, 2018

Duh, looks like we said this in #711. So, closing this because whatever we do for this would be in #711.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment