next-runtime icon indicating copy to clipboard operation
next-runtime copied to clipboard

Support 3rd party redirects

Open AndrewIngram opened this issue 3 years ago • 6 comments

For things like billing flows, its not uncommon to want to redirect-after-POST to a 3rd party endpoint. next-runtime doesn't appear to support returning external URLs with its redirect handler.

I've even tried things like the following:

res.setHeader("location",  someExternalURL);
res.statusCode = 307;
res.end();
return;

But I get this error:

error - Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client

Right now, our workaround is to return json and update window.location.href during render. It'd be a little neater if this scenario could be detected and managed automatically.

AndrewIngram avatar Mar 21 '22 13:03 AndrewIngram

Are you sure it's a next-runtime thing, that's bugging? I'm able to redirect to my own site without trouble:

https://codesandbox.io/s/next-runtime-cookie-forked-ko4iob?file=/pages/index.js

https://user-images.githubusercontent.com/1196524/159327690-a7967632-4f41-4954-ab8f-535b5c415034.mov

smeijer avatar Mar 21 '22 17:03 smeijer

What about with the form hook?

AndrewIngram avatar Mar 21 '22 17:03 AndrewIngram

Got it! Yeah, that's a bug that needs to get squashed. Thanks!

smeijer avatar Mar 21 '22 17:03 smeijer

Ok, so when I'm trying to redirect using Form, it does work, but only if the receiving endpoint has CORS properly configured.

This works:

export const getServerSideProps = handle<PageProps>({
  async get() {
    return json({});
  },

  async post() {
    return redirect('https://swapi.dev/api/people/1/');
  },
});

export default function FormWithRemoteRedirect() {
  return (
    <Form method="post">
      <button id="submit" type="submit">
        Submit
      </button>
    </Form>
  );
}

Replace the destination with https://meijer.ws, and it does not.

The problem is that the fetch request already handles the redirect. If that causes an error, such as 'NO CORS POLICY', the browser redirect won't be reached.

So we need to find a way to take over the redirect, and handle it manually. Good thing, fetch has an option for that:

return fetch(url.toString(), {
  method,
  headers: { accept: 'application/json' },
  body: data,
+ redirect: 'manual',
});

Bad thing, when using redirect: manual, the response will get a type: "opaqueredirect" property, but (Location) headers won't be available. So, I'm leaning to supporting a "manual" redirect. Something like (pseudo):

<Form 
  action="/" 
  method="post" 
  redirect="manual" 
  onSuccess={() => window.location = 'https://remote'} 
/>

That is unless we can find a way to catch the fetch error and determine that it was a failed redirect. To simplify the API we could also make it like:

<Form 
  action="/" 
  method="post" 
  redirect="https://remote" 
/>

smeijer avatar Mar 21 '22 18:03 smeijer

FWIW, for my current use case, we don't know the URL up front, we call the 3rd party in the post handler and it gives us back the URL to redirect to.

AndrewIngram avatar Mar 21 '22 18:03 AndrewIngram

Will it still happen even if we use standard Next.js object with this shape?

{
    redirect: {
        destination: "/sign",
        permanent: false,
    },
}

See https://nextjs.org/docs/api-reference/data-fetching/get-server-side-props#redirect

BleedingDev avatar Oct 21 '22 20:10 BleedingDev