Shopify / shopify-express

“Get up and running quickly with Express.js and the Shopify API.”
MIT License
137 stars 87 forks source link

EASDK auth flow is at risk of breaking in future versions of Chrome #65

Open jamiemtdwyer opened 6 years ago

jamiemtdwyer commented 6 years ago

Logging this now while it's fresh in my mind

https://github.com/Shopify/shopify-express/blob/master/routes/shopifyAuth.js#L37-L47

This code is at risk of breaking in a future version (65?) of Chrome, where Chrome will prevent redirects in the parent window.

The solution is to use a postMessage to the Shopify EASDK in order to perform the redirect. See the following example from shopify_app:

https://github.com/Shopify/shopify_app/blob/3fb589d71bc03a11a8bb48bf87e613f6ce0210ea/app/assets/javascripts/shopify_app/redirect.js

marutypes commented 6 years ago

We should update this asap

marekweb commented 6 years ago

Take a look at my module which is a drop-in solution for this. It uses the postMessage solution.

https://github.com/marekweb/shopify-express-oauth-redirect

jamiemtdwyer commented 6 years ago

Thanks for that @marekweb.

I think that ideally, we'd make this available in such a way that it's not specific to OAuth redirects, but rather give consumers a method to invoke (or perhaps an additional method) to perform a full-page redirect any time it's needed – the only other scenario I can think of off-hand would be a redirect to the RecurringApplicationCharge acceptance screen.

ivnnv commented 6 years ago

Hi, This is indeed interesting and having a specific method will be useful to bypass using window.top.location.href 'hack'. Attaching the code as we are using at the moment in a selection plans page which we need the user be taken to the window.top.location +'/client/plan-select' as theres the express router listening for the method with the logic for the charge acceptance screen (just in case its helpful to show how real-world shopify apps are at the moment solving this missing functionality).

<CalloutCard
    title={'Trial'}
    primaryAction={{
        content: '3-Day Trial: $0,00',
        onAction: () => window.top.location.href = '/client/plan-select?plan_name=trial'
    }}
>
jamiemtdwyer commented 6 years ago

The problem with window.top.location is that there is a discussion around this not being supported in future versions of Chrome, in order to prevent cases where annoying advertisements can redirect the parent window. This change was originally targeted for Chrome Canary (v65), but I believe the Chrome team has held off on this. Based on what I've read / heard, it might be included in the final release version of Canary as an optional user configuration.

Sorry I don't have any links handy at the moment pointing to the discussions from the Chrome team, but I'll try to grab them when I have time.

marekweb commented 6 years ago

@jamiemtdwyer Good point about other the use cases for the redirect. The function in shopify-express-oauth-redirect which does that is called createRedirectBody and it generates a redirect for any arbitrary URL.

Here's a usage example.

const createRedirectBody = require('shopify-express-oauth-redirect/create-redirect-body');

const redirectBody = createRedirectBody('https://example.com/arbitrary/url', 'shiny-trinkets.myshopify.com');

res.send(redirectBody);

Here is the contents of redirectBody in this example. The implementation is based on the same approach as in inshopify_app.

 <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="utf-8" />
        <base target="_top">
        <title>Redirecting...</title>
        <script type="text/javascript">
          // If the current window is the 'parent', change the URL by setting location.href
          if (window.top == window.self) {
            window.top.location.href = "https://example.com/arbitrary/url";
          // If the current window is the 'child', change the parent's URL with postMessage
          } else {
            normalizedLink = document.createElement('a');
            normalizedLink.href = "https://example.com/arbitrary/url";
            data = JSON.stringify({
              message: 'Shopify.API.remoteRedirect',
              data: { location: normalizedLink.href }
            });
            window.parent.postMessage(data, "https://shiny-trinkets.myshopify.com");
          }
        </script>
      </head>
      <body>
      </body>
    </html>