Skip to content

Unable to redirect from non-Blazor endpoint to different-origin URL during enhanced navigation #50384

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

Closed
1 task done
darena-pjindal opened this issue Aug 28, 2023 · 13 comments
Assignees
Labels
area-blazor Includes: Blazor, Razor Components blog-candidate Consider mentioning this in the release blog post enhancement This issue represents an ask for new feature or an enhancement to an existing one
Milestone

Comments

@darena-pjindal
Copy link

Is there an existing issue for this?

  • I have searched the existing issues

Describe the bug

I have built an application that has both razor and MVC components. I have a MVC controller called Test with a default method called index. I want to browse from a razor component to this page. I added an anchor tag in the razor component Test. When I click on test, the browser is updated with the URL, but the page doesn't reload. I understand that this could be resolved by using NavigationManage with forceload =true. However, I am using Server Side Rendering for this component, so I can't call an onclick function. I also tried adding target="_self" but that doesn't seem to work either. Is there any way to forceload the page? Or, is there another recommended solution to navigate between razor and mvc components when using Server Side Rendering.

Expected Behavior

If I have a MVC controller, i should be able to navigate to it using an anchor tag that would refresh the page.

Steps To Reproduce

No response

Exceptions (if any)

No response

.NET Version

No response

Anything else?

No response

@ghost ghost added the area-blazor Includes: Blazor, Razor Components label Aug 28, 2023
@SteveSandersonMS
Copy link
Member

Does target="_top" work?

Also if you could post a minimal repro that would be very helpful, as it's not clear to me why this shouldn't work by default anyway.

@mkArtakMSFT
Copy link
Contributor

Thanks for contacting us.
What seems to be happening here is that enhanced navigation is blocking the actual navigation to happen. That is going to be addressed as part of #50290.
In the meantime, you should be able to address this by disabling enhanced navigation. To do so, try to update the script tag which references the blazor.web.js as follows:

<script src="blazor.web.js" autostart="false"></script>
<script>
Blazor.start({ ssr: {disableDomPreservation: true }} );
</script>

@mkArtakMSFT mkArtakMSFT added the Needs: Author Feedback The author of this issue needs to respond in order for us to continue investigating this issue. label Aug 29, 2023
@ghost
Copy link

ghost commented Aug 29, 2023

Hi @darena-pjindal. We have added the "Needs: Author Feedback" label to this issue, which indicates that we have an open question for you before we can take further action. This issue will be closed automatically in 7 days if we do not hear back from you by then - please feel free to re-open it if you come back to this issue after that time.

@darena-pjindal
Copy link
Author

Does target="_top" work?

Also if you could post a minimal repro that would be very helpful, as it's not clear to me why this shouldn't work by default anyway.

Thanks @SteveSandersonMS . The target="_top" worked like magic. Is that the recommended way ? I have uploaded a simple repro here. If you remove the target tag from button on the index page, it stops working. https://github.com/darena-pjindal/BlazorApp6

@darena-pjindal
Copy link
Author

Thanks for contacting us. What seems to be happening here is that enhanced navigation is blocking the actual navigation to happen. That is going to be addressed as part of #50290. In the meantime, you should be able to address this by disabling enhanced navigation. To do so, try to update the script tag which references the blazor.web.js as follows:

<script src="blazor.web.js" autostart="false"></script>
<script>
Blazor.start({ ssr: {disableDomPreservation: true }} );
</script>

Hi @mkArtakMSFT - It seems to work using the target=_top as suggested by Steve. Do you recommend disabling enhanced navigation

@SteveSandersonMS
Copy link
Member

Do you recommend disabling enhanced navigation

In general no, it's better to have it on, otherwise you can't preserve interactive components as you navigate around.

Are you able to post a minimal repro of the scenario that doesn't work? I just set up a .NET 8 Blazor Web site that contains a non-Blazor rendered page and enhanced nav was able to navigate to it. It would be helpful to understand what's different about your scenario where it doesn't work. Thanks!

@ghost ghost added Needs: Attention 👋 This issue needs the attention of a contributor, typically because the OP has provided an update. and removed Needs: Author Feedback The author of this issue needs to respond in order for us to continue investigating this issue. labels Aug 29, 2023
@SteveSandersonMS
Copy link
Member

Oh sorry, I just saw your comment above with the repro link. I'll have a look!

@SteveSandersonMS
Copy link
Member

OK, I see this is specific to issuing redirections to external URLs (i.e., on a different origin) from a non-Blazor-rendered endpoint. That's not currently supported by enhanced nav since it would need the server not to return a 301/302, since the client-side code that issued the fetch could neither follow the redirection nor even find out the destination URL since it's on a different origin.

Possible solution

If we added the following middleware, that would detect all redirections that occur during enhanced nav requests and turn them into responses that work on the client:

app.Use(async (HttpContext ctx, RequestDelegate next) =>
{
    await next(ctx);

    var redirectionUrl = ctx.Response.Headers.Location.Count > 0 ? ctx.Response.Headers.Location.ToString() : null;
    if (!string.IsNullOrEmpty(redirectionUrl) && ctx.Request.Headers.ContainsKey("blazor-enhanced-nav") && IsPossibleExternalDestination(ctx.Request, redirectionUrl))
    {
        ctx.Response.Headers.Add("blazor-enhanced-nav-redirect-location", redirectionUrl);
        ctx.Response.StatusCode = 200;
    }

    static bool IsPossibleExternalDestination(HttpRequest request, string destinationUrl)
    {
        if (!Uri.TryCreate(destinationUrl, UriKind.Absolute, out var absoluteUri))
        {
            return false;
        }

        return absoluteUri.Scheme != request.Scheme
            || absoluteUri.Authority != request.Host.Value;
    }
});

It still wouldn't work if the response had already started, but that's relatively uncommon.

So, we have to decide:

  • Is it important to support this? (As opposed to telling people to use target=_top or whatever other link-specific way we offer to disable enhanced nav on those specific links)
  • Are we willing to add such middleware by default?
  • Or is there any other way that the JS code could honor the redirection coming back from its fetch request, i.e., actually navigate to it, regardless of CORS?

@darena-pjindal
Copy link
Author

Thanks, @SteveSandersonMS . I didn't realize that this was happening due to redirection to external URLs. My scenario was that I needed to do a custom redirect for login/logout scenario to an OIDC server. I was able to confirm that I can add any MVC view or even RazorPage and it works without issues. Thanks so much for the quick responses.

@SteveSandersonMS
Copy link
Member

Thanks for confirming. I'll reopen this to track the fact there's an open question about support for this.

@SteveSandersonMS SteveSandersonMS changed the title Unable to navigate to an action in controller from Razor component in Blazor Web app Unable to redirect from non-Blazor endpoint to different-origin URL during enhanced navigation Aug 30, 2023
@SteveSandersonMS
Copy link
Member

SteveSandersonMS commented Aug 31, 2023

Plan

We had a long discussion about this that concluded as follows:

  1. We are going to make external redirections from enhanced navigation work from all server-side code, not only Blazor endpoints.
    • This will likely be done as middleware added through a startup filter (did I get that right?) but there is still some discussion about whether it could be a matcher policy.
    • It will not disclose the URL of the redirection to the client. It will just return a 200 or something similar, containing a header to indicate that some external redirection is required. This will be super light and targeted, as it only does something when all these are true:
      • It's a GET request
      • It has the blazor-enhanced-nav: on header
      • The response is a 30x
      • The response.Location is on an external origin
    • The client will handle this by doing location.href = originalUrl to perform a full-page navigation. This means there will be a duplicate request, but this time without enhanced nav so the redirection (or other response) will be handled as normal.
    • We consider the duplicate request acceptable because it's GET and it's the only practical option besides letting it fail. Developers can't realistically know all the reasons why some cross-cutting middleware might do a 302 to an external URL, so it's not fair to say they should have disabled enhanced navigation for this particular link.
  2. We will remove the existing Blazor-specific logic that detects external redirections and hence will only have the above implementation.
  3. For enhanced form submissions, we will just let it fail. There's no other option since we can't safely retry POST requests, and we can't disclose the external redirection URL to the client-side JS code.
    • Update: I propose a modification to this plan. The middleware from (1) above should also run for POST requests. But when the client see the special "needs external redirection" header for a POST request, it does not retry (that would be unsafe) but instead destroys the <form> in some way (e.g., removes it from the DOM) and displays the yellow "error" bar.
    • ... because otherwise, imagine it was a "purchase item" form that does a post-redirect-get. If we don't follow the redirect and don't display anything in the UI, the user will just assume they didn't really click submit and will submit again, and buy again. We have to stop them from manually duplicating the POST - it has to be treated as a fatal error.
  4. For all enhanced nav requests that fail, we'll also log some additional message saying that this was an enhanced nav request and the developer might want to try disabling enhanced nav for this specific link/form, with link to docs.

@halter73
Copy link
Member

  • Update: I propose a modification to this plan. The middleware from (1) above should also run for POST requests. But when the client see the special "needs external redirection" header for a POST request, it does not retry (that would be unsafe) but instead destroys the <form> in some way (e.g., removes it from the DOM) and displays the yellow "error" bar.

I think this is the right idea. I might even consider something more dramatic like clearing the entire page and showing nothing but the error bar rather leaving the app in an unexpected state. I do think that this will be somewhat common when cookies expire even for a properly configured app in production, so it is sad

It's almost tempting to change the POST to a plain GET without parameters and hope we get the redirect anyway. My guess is 99% of the time it will be an OIDC redirect and this would be equivalent to what a Razor Page form would do, but it's not fundamentally sound. We don't even know if a GET version of the endpoint exists.

@SteveSandersonMS SteveSandersonMS self-assigned this Sep 6, 2023
@SteveSandersonMS SteveSandersonMS added this to the 8.0-rc2 milestone Sep 6, 2023
@mkArtakMSFT mkArtakMSFT added blog-candidate Consider mentioning this in the release blog post and removed Needs: Attention 👋 This issue needs the attention of a contributor, typically because the OP has provided an update. labels Sep 11, 2023
@SteveSandersonMS
Copy link
Member

Fixed in #50551. These kinds of redirections will now just work, as long as it's not a POST request.

@danroth27 danroth27 added the enhancement This issue represents an ask for new feature or an enhancement to an existing one label Oct 4, 2023
@ghost ghost locked as resolved and limited conversation to collaborators Nov 4, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-blazor Includes: Blazor, Razor Components blog-candidate Consider mentioning this in the release blog post enhancement This issue represents an ask for new feature or an enhancement to an existing one
Projects
None yet
Development

No branches or pull requests

5 participants