0

tl;dr; How do I get Chrome to follow the 302 'Location' redirect to a different domain after an HTTP POST?

I'm using OpenID Connect, using an external provider to provide authentication services to my ASP.NET MVC (C#, .NET 6) application.

I have a controller action to close an account. In this instance,

[HttpPost("close-account")]
public async Task<IActionResult> CloseAccount()
{    
   // This does not work as expected in a POST and the browser does not redirect
   var properties = new AuthenticationProperties { RedirectUri = "..." }
   return SignOut(properties, "Cookies", "OpenIdConnect");
}

There is a standard html <form> which is completing the post action to the controller via a <button>. No Javascript involved.

Looking at the Chrome developer tools, the browser receives the 302 Found with the Location response header correctly set to the URL the browser needs to redirect to in order to complete the sign out with the OpenID Connect provider.

However, the browser does not follow the redirect. I am presuming because it is cross-origin, and starts with "https://oidc.myauthenticationprovider.com/logout?...." which is a different domain.

I have verified this - because if I change the redirect to be another URL in the same site, then the browser follows up with a GET to the URL provided in the Location header. It's just that if the origin is a different domain, it does nothing.

I only see this behaviour with POST. I have a similar GET endpoint to sign out users (without closing their account- just a regular sign-out) - which works perfectly.

[HttpGet("sign-out")]
public async Task<IActionResult> CloseAccount()
{
   // This works as expected in a GET and the user is redirected.
   var properties = new AuthenticationProperties { RedirectUri = "..." }
   return SignOut(properties, "Cookies", "OpenIdConnect");
}

I'm not sure this is strictly a CORS issue because it is the response to an HTTP POST rather than an XHR request, however, I have tried the below in case it is related to CORS:

  • I have tried the services.AddCors method in the application startup, adding the origin
  • I have tried manually adding the Access-Control-Allow-Origin header to both the original GET and also the POST response of the close account page

These do not make a difference.

To the user, the behaviour is as if the form has done nothing. They press the button, and it seems like nothing at all happens. The code within the controller executes (in my example, the account does get closed), but the redirection is then ignored.

bgs264
  • 4,572
  • 6
  • 38
  • 72
  • Requests made during an OpenID Connect flow should never be cross-origin, because they should be made in a visible browser tab, not as `fetch` requests, see [here](https://stackoverflow.com/questions/72382892/access-to-fetch-at-https-accounts-google-com-o-oauth2-v2-auth-has-been-blocked/72392743#72392743). – Heiko Theißen Feb 01 '23 at 15:14
  • Thanks @HeikoTheißen - I think I am trying to keep the flow in visible browser tabs - it's just that in the 'Close account' action, I want to perform some activity _before_ redirecting to the OpenID Connect signout page. – bgs264 Feb 01 '23 at 15:33
  • I'd like to perform some action server-side (closing an account) and then redirect the user to the OpenID Connect's sign-out page. – bgs264 Feb 02 '23 at 08:05
  • But that is no reason to make the POST request as a `fetch` request. You can make it by submitting an HTML form. – Heiko Theißen Feb 02 '23 at 09:21
  • That's exactly what I am doing. Submitting an HTML form. Just a standard, traditional, `HTTP POST`. The browser receives a response back from the POST with a 301 status code and the correct Open ID Connect Signout URL in the `Location` header, but it does not follow it. No `fetch` request involved. – bgs264 Feb 02 '23 at 10:30
  • 302. Sorry for the typo in the question. It returns '302 Found' with a Location header set to the Open ID Connect sign out URL, but the browser does not follow it. Doing the same thing with a GET request also returns the same 302 Found with the same Location header, but the browser _does_ follow it. The difference is between the GET and the POST. – bgs264 Feb 02 '23 at 14:21
  • It's also worth noting that if the value in the ```Location``` header relates to the same site, e.g. not a different domain, the browser will happily follow it with a ```GET``` to the URL provided. Its only when that value is a _different domain_ that it refuses to follow it up with the ```GET``` to the URL provided. – bgs264 Feb 02 '23 at 14:31
  • What _does_ the browser show, then, after you submit the HTML form? An empty page? – Heiko Theißen Feb 02 '23 at 14:57
  • It does nothing. To the user it would seem that nothing happened. The actual code in the ```POST``` method executes - e.g. the account successfully closes, but the browser does _nothing_. You're left on the same page looking at the button you just clicked thinking it's not done anything. – bgs264 Feb 02 '23 at 15:41
  • But you see the 302 status and Location header in the DevTools > Network tab? Can you include the client side (UI) code for submitting the form in your question, please? – Heiko Theißen Feb 02 '23 at 15:46
  • It's a plain old ```
    ``` tag with a plain old `````` button. There's no specific UI code that submits the form. Yes - you see the 302 status and Location header in the DevTools>Network tab.
    – bgs264 Feb 02 '23 at 15:50
  • Aha - @HeikoTheißen it was due to us setting the ```form-action: self``` directive in CSP. Note the warning at the top of the page: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/form-action - Chrome 63 will block a redirect after a form submission whereas Firefox 57 will not. – bgs264 Feb 03 '23 at 08:58

1 Answers1

1

This was due to us setting the form-action: self directive in CSP. As described on MDN Web Docs,

Warning: Whether form-action should block redirects after a form submission is debated and browser implementations of this aspect are inconsistent (e.g. Firefox 57 doesn't block the redirects whereas Chrome 63 does).

For us this was what was causing the blocked redirect to a different domain after form submission.

bgs264
  • 4,572
  • 6
  • 38
  • 72
  • Interesting. But even `form-action 'self'` blocks only HTTP redirects, not Javascript redirects, similar to https://stackoverflow.com/questions/75184259/samesite-strict-cookies-and-cross-site-requests-with-redirections. – Heiko Theißen Feb 03 '23 at 09:20
  • There was no JavaScript involved in my question, @HeikoTheißen . We are not using JavaScript. – bgs264 Feb 03 '23 at 15:11