-
Notifications
You must be signed in to change notification settings - Fork 415
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
Turbo Drive treats external URLs redirected from same-origin URLs as same-origin #401
Comments
Turbo Drive tells its underlying turbo/src/http/fetch_request.ts Line 120 in c9c1c11
I think the problem is that Turbo can't know ahead of time if the page will redirect to a third party URL. So it has no way of knowing it if should send a https://developer.mozilla.org/en-US/docs/Web/API/fetch suggests there's an alternative option ( |
Makes sense, I figured it would be something to do with automatically follwing redirects. If someone (maybe me, not sure if I have the time) did contribute a pull request which changed the behaviour, would it be likely to be accepted or would it be rejected due to adding too much complexity for a corner case like this? I'm fine with my solution of disabling Turbo if it wouldn't, but it might be worth at least updating the docs to mention this corner case. |
I think a docs PR is a good start. I’m not a maintainer so can’t answer the other question. My gut feel is this isn’t a very common case, as you say. |
I have the same problem. In my case I want to redirect users to an external payment provider during the checkout, and the URL has to be generated on the server. Disabling Turbo technically works, but then I am also losing the ability to disable the button using the turbo events (to prevent double clicking), for example. My current workaround is to use Stimulus and a turbo stream: import { Controller } from 'stimulus'
export default class extends Controller {
static values = {
url: String
}
connect() {
// add error checking as you see fit
window.location.href = this.urlValue
}
} <%= turbo_stream.update :payment do %>
<%= tag.div nil, data: {
controller: "location-href",
location_href_url_value: @url
} %>
<% end %> The page with the button has an empty |
Requesting access to Pocket account implies a redirection to an external domain (i.e. getpocket.com). The form was handled by Turbo Drive, which cannot access this domain because of the `Content-Security-Policy` and `Access-Control-Allow-Origin` policies. Thus, Turbo Drive needs to be disabled on this form. The bug has been introduced by d65c702. Reference: hotwired/turbo#401
Requesting access to Pocket account implies a redirection to an external domain (i.e. getpocket.com). The form was handled by Turbo Drive, which cannot access this domain because of the `Content-Security-Policy` and `Access-Control-Allow-Origin` policies. Thus, Turbo Drive needs to be disabled on this form. The bug has been introduced by d65c702. Reference: hotwired/turbo#401
We also experience this issue. We use Ideally Turbo handles a redirect to a cross-origin location correctly. This doesn't seem to be that easy because with a I'd love to hear @dhh his opinion on this. I expect a lot of Rails users upgrading to Turbo going to experience this issue. Maybe we should at least update the documentation for this, but maybe there are other approaches possible? |
I wanted to leave a more complete workaround for what I'm currently doing, as it sounds like it would be helpful. We have a The way I've built ours is using a form with Turbo disabled and using # payments_controller.rb
class PaymentsController
def create
session = Stripe::Checkout::Session.create(...)
redirect_to session.url, status: :see_other
end <%# views/payments/new.html.erb %>
<%= form_with url: payments_path, method: :post, local: true, data: {turbo: false} do |form| %>
<%# Any other form fields %>
<%= form.submit "Checkout" %>
<% end %> Basically the form is turned into just a standard HTML form since all the magic is disabled. The main issue with this is it isn't possible to update just the form after an error since it's just a regular HTML form. |
In my case, a form with method POST and an external action is also processed through Turbo. This shouldn't be happening, according to this?:
|
@boboldehampsink that sounds like a different issue, this issue is specifically about redirects from same-origin to external. I suggest you open a new issue. |
@TastyPi thanks for the workaround! Worked for us |
We also have an issue with this problem. Similar to others, we have a form which can POST and update data. When there are errors we want Turbo to update the form for the user, but on success we want to follow a returned redirect to an external URL. So disabling Turbo on this form fixes the redirect, but it does not provide a very good experience when there are form errors. |
@andrewjtait we have a new solution that allows us to do both. We created a very basic Stimulus controller that uses JavaScript to redirect, and we use Turbo to add an element using that controller to the DOM export default class extends Controller {
static values = {url: String}
def initialize() {
window.location.href = this.urlValue
}
} <%# create.turbo_stream.erb %>
<%= turbo_stream.append "form" do %>
<%= tag.div data: {controller: "redirect", redirect_url_value: @url} %>
<% end %> It's not ideal, but it works. |
I created a small Rails apps with examples of how to set up redirection at https://github.com/TastyPi/turbo-forms |
I ended up here after attempting to change our existing Stripe Checkout client-side redirect (redirectToCheckout, which used to be their only option available) into a server-side redirect (as shown above). Our intention is to avoid the extra render and accelerate the transition to the checkout page. We want to keep XHR in the order form to allow displaying validation errors (so data-turbo should be true), but if validation passes it should redirect to the external Stripe checkout page. As noted here however, we encounter 403 Forbidden, "CORS Missing Allow Origin": |
@sedubois we have the exact same use case. Check out the repo I linked to in #401 (comment). TL;DR is we fixed this by creating a Stimulus controller that uses JavaScript to redirect to the Stripe URL. You can use Turbo Streams to add a div to the DOM that uses the Stimulus controller to redirect. You have a form: <%= form_with ..., id: "my-form" do %>
...
<% end %> On success, render the following: <%= turbo_stream.append "my-form" do %>
<div controller="redirect" data-redirect-url="<%= stripe_checkout_url %>"></div>
<% end %> On error, render the following: <%= turbo_stream.replace "my-form" do %>
<%= render "form" %>
<% end %> You shouldn't use I don't know if the Hotwired team have any plans to support this natively, but it works perfectly for us. |
Yes @TastyPi, thanks for the recap. This looks similar to what we already have: redirect will happen client-side anyway. But it's true that it should allow us to stop relying on the legacy UJS and start using Turbo for the API. So we'll give it another try. |
If anyone wants to summarize these challenges in a doc PR, that would be a good start. It's a basic CORS restriction, so I think treating validation errors with JS and then using vanilla forms for the redirect is a good path to suggest. |
Couldn't this be automated via error handling within Turbo instead? |
A simple solution is to use Turbo Power's redirect_to Stream Action. respond_to do |format|
format.html { redirect_to url, allow_other_host: true }
format.turbo_stream { render turbo_stream: turbo_stream.redirect_to(url) }
end In our case we have a Stripe Checkout flow that requires you to sign in / sign up before proceeding to Stripe for payment. Redirecting to Stripe caused a CORS error, but we didn't want to have to disable Turbo on every form in the sign in / sign up flow just because it might inevitably redirect to Stripe. Instead, the action that does redirect to Stripe now returns a Turbo Stream to tell the browser to navigate to the Stripe Checkout URL. Turbo is on the whole time, and no CORS errors happen. |
According to the Turbo Drive docs https://turbo.hotwired.dev/handbook/drive#setting-a-root-location:
However, I have encountered two scenarios where this is not the case:
In both cases, Turbo Drive attempted to load the external URL, which in my particular case causes a CORS error due to cross-origin requests not being allowed by the external page. I've fixed both cases by adding
data-turbo=false
to the<form>
and<a>
respectively, but based on the wording of the documentation I would have expected them to both fallback to a full page load.Is it possible for Turbo Drive to handle redirects in the expected way?
The text was updated successfully, but these errors were encountered: