menu

Questions & Answers

How can I respond to a browser POST request in a way that does not cause navigation?

tl;dr -- Is there a kind of response I can send to a browser's POST request (from my web server running Node.js/Express.js) that will prevent the browser from navigating to a new page and breaking any open WebSocket connections?

I am implementing the new Sign In With Google OAuth flow from a pure client-sided React app that uses WebSocket for bi-directional communication.

The new Sign in With Google flow uses either a pre-loaded HTML or JavaScript component to render a button for logging in. Clicking the button opens a new window where the user can consent to sharing data and either log in with a Google account or select an existing logged-in account.

Once complete, the Sign-In With Google flow will provide an identity token either via a callback function or a POST request to specified URL. I want to use the second approach to POST the token to a https://my-server/googleLoginComplete endpoint. The endpoint will perform some internal login and session operations, and then communicate to the browser/client via an open WebSocket connection that the login operation is complete.

I have everything working, except for one issue. Google's API is performing a form POST to send the identity token to the developer supplied endpoint, and any response that I return from that endpoint will cause a navigation change in the browser which breaks the web socket connection.

If I had control over the Sign In With Google API, I could do the equivalent of a form.submit.preventDefault() and instead just use a fecth or xmlHttpRequest to perform the POST without navigating to a new page. Unfortunately, Google does not provide this or a similar option, nor do they even have the source code for their new client publicly available.

I've tried sending a 204 and 304 response, an empty object, a generic 200, sending a redirect back to the same URL the browser is already on, not sending anything back at all, destroying the resp completely, and some other variations. But I can't find anything that will simply result in the browser doing nothing. And the web socket connection remains open.

Here is some skeleton code:

// React: '/login.js'

export default function Login() {
  // ...

  return (
    <Row>
      <div id="g_id_onload"
           data-client_id="my-client-id"
           data-login_uri="http://localhost:5000/googleLoginComplete"
           data-auto_prompt="false">
      </div>
      <div className="g_id_signin"
           data-type="standard"
           data-size="large"
           data-theme="outline"
           data-text="sign_in_with"
           data-shape="rectangular"
           data-logo_alignment="left">
      </div>
    </Row>
  )
}
// Node.js/Express.js: 'app.js'

//...
app.post('/googleLoginComplete', (req, res) => {
  const googleIdentToken = req.body.credential;

  const sessionId = doSomeInternalSessionStuff(googleIdentToken);
  socket.emit('login.success', {sessionId});

  // Something here to just cause the browser to do nothing
  res.send('ignore this and dont do any navigation');
});

(I know that I can change the Sign In With Goole method to use a callback instead, but to keep this consistent with other federated auth providers, I want to use the URL redirect option).

Comments:
2023-01-07 20:38:09
Further investigation shows that both Firefox and Chrome are unloading the page (which I can see by adding a window.onbeforeunload handler) in response to the POST request, even when then response is a 204 (which according to multiple sources should not cause a page navigation).
Answers(1) :

Handle a form submission in React without causing the page to navigate or refresh:

function handleSubmit(event) {
  event.preventDefault();
  // Handle the form submission here
}

return (
  <form onSubmit={handleSubmit}>
    {/* Form elements go here */}
    <button type="submit">Submit</button>
  </form>
);

Alternatively, you could use a button element with a click event handler to submit the form, instead of using the submit button. This will also allow you to handle the form submission without causing the page to navigate or refresh.

function handleClick() {
  // Handle the form submission here
}

return (
  <form>
    {/* Form elements go here */}
    <button type="button" onClick={handleClick}>Submit</button>
  </form>
);