Skip to content

signInWithPopup issue with Firefox and beforeSignIn blocking function #6956

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
silas opened this issue Jan 19, 2023 · 13 comments
Closed

signInWithPopup issue with Firefox and beforeSignIn blocking function #6956

silas opened this issue Jan 19, 2023 · 13 comments

Comments

@silas
Copy link

silas commented Jan 19, 2023

Describe your environment

  • Operating System version: MacOS 13.1
  • Browser version: Firefox 109.0
  • Firebase SDK version: 9.15.0
  • Firebase Product: auth

Describe the problem

When signInWithPopup is used with Firefox (couldn't reproduce in Chrome or Safari) and there is a beforeSignIn blocking function that takes longer than ~2 seconds to execute the signInWithPopup call will throw the auth/popup-closed-by-user error.

Steps to reproduce:

  1. Checkout the POC code from here
  2. Edit the initializeApp call in public/index.html to contain valid settings
  3. Deploy to a project that has Authentication with Identity Platform enabled
  4. Ensure Google sign-in method is enabled
  5. Go to <YOUR-NAME>.firebaseapp.com in Firefox and click Sign in with Google
  6. Notice the alert Firebase: Error (auth/popup-closed-by-user).
  7. Disable the beforeSignIn blocking function via Authentication -> Settings -> Blocking functions
  8. Notice that it works

Relevant Code:

Blocking function

const { runWith } = require("firebase-functions");

exports.beforeSignIn = runWith({ memory: "128MB" })
  .auth.user()
  .beforeSignIn(async (user, context) => {
    await new Promise((resolve) => {
      setTimeout(resolve, 3000);
    });
  });

HTML

<!DOCTYPE html>
<html>
  <body>
    <form id="signIn">
      <button type="submit">Sign in with Google</button>
    </form>

    <script type="module">
      import { initializeApp } from "https://www.gstatic.com/firebasejs/9.15.0/firebase-app.js";
      import {
        GoogleAuthProvider,
        getAuth,
        signInWithPopup,
      } from "https://www.gstatic.com/firebasejs/9.15.0/firebase-auth.js";

      const app = initializeApp({
        appId: "<APP-ID>",
        apiKey: "<API-KEY>",
        authDomain: "<NAME>.firebaseapp.com",
        projectId: "<PROJECT-ID>",
      });
      const auth = getAuth(app);

      document.getElementById("signIn").addEventListener("submit", (event) => {
        event.preventDefault();

        signInWithPopup(auth, new GoogleAuthProvider())
          .then((result) => {
            alert("OK");
          }).catch((error) => {
            alert(error.message);
          });
      });
    </script>
  </body>
</html>

Quickly looking through the js-sdk code, there seems to be a 2 second timeout here, which might be related:

Logs:

[2023-01-19T04:08:41.573Z]  @firebase/auth: Auth (9.15.0): INTERNAL ASSERTION FAILED: Pending promise was never set
[2023-01-19T04:08:41.575Z]  @firebase/auth: Auth (9.15.0): INTERNAL ASSERTION FAILED: Pending promise was never set
Uncaught (in promise) Error: INTERNAL ASSERTION FAILED: Pending promise was never set
    K instantiator.ts:31
    $ instantiator.ts:34
    reject abstract_popup_redirect_operation.ts:136
    onAuthEvent abstract_popup_redirect_operation.ts:113
    sendToConsumer auth_event_manager.ts:105
    onEvent auth_event_manager.ts:69
    onEvent auth_event_manager.ts:67
    initAndGetManager popup_redirect.ts:137
    lm cb=gapi.loaded_0:232
    mm cb=gapi.loaded_0:232
    Gk cb=gapi.loaded_0:209
    Pk cb=gapi.loaded_0:209
    jk cb=gapi.loaded_0:210
    TD cb=gapi.loaded_0:203
    qe cb=gapi.loaded_0:107
    re cb=gapi.loaded_0:107
    pk cb=gapi.loaded_0:202
    <anonymous> cb=gapi.loaded_0:213
    Za api.js:26
    Ya api.js:29
    Za api.js:26
    za api.js:28
    D api.js:28
    v api.js:29
    ca api.js:29
    <anonymous> cb=gapi.loaded_0:1
instantiator.ts:31:45
@prameshj
Copy link
Contributor

The popup closed error is thrown from here -

if (this.authWindow?.window?.closed) {
, if the window becomes closed. I suspect Firefox might be causing this to become true, if it senses the popup to be unresponsive somehow.

We will try to repro with the attached snippet(thanks!), but it might be a browser-specific behavior that we might not be able to detect/control in the SDK.

@benjichat
Copy link

benjichat commented Feb 20, 2023

Firebase SDK version: 9.15.0
MacOS: 13.1

I have been able to reproduce this error using chrome v 107.0.5304.87

When I updated to v 110.0.5481.100 - the error went away.

Not sure if this is helpful - it's one data point more

@tanuj101
Copy link

tanuj101 commented Mar 5, 2023

any updates on this?

@prameshj
Copy link
Contributor

prameshj commented Mar 6, 2023

Thanks for the update. Are we still seeing this in the latest Firefox 110?

As mentioned above, the SDK gets a popup closed event and reacts to it, so it likely has a browser interaction at play here.

@silas
Copy link
Author

silas commented Mar 7, 2023

@prameshj Yes, I'm still seeing this issue.

It seems like this might be an issue in /__/auth/handler.

After a little debugging I found this to be the current series of events:

  1. Initiate login via popup
  2. Popup window is shown
  3. User completes oauth flow
  4. Popup window redirects to /__/auth/handler
  5. Popup window immediately closes after redirect
  6. POST to https://identitytoolkit.googleapis.com/v1/accounts:signInWithIdp is executed from the main window (this is the slow call)
  7. Popup strategy times out and error is thrown
  8. POST to https://identitytoolkit.googleapis.com/v1/accounts:lookup succeeds
  9. User is signed in

If I monkey patch the javascript environment during the execution of /__/auth/handler to disable window.close (e.g. window.close = function() {}) the popup window isn't closed.

This leads me to believe that the close event is being called by some code in /__/auth/handler.

It'd be interesting for someone with access to that code to see if there are any cases where it will close immediately or in the event of specific failures (e.g. the gapi iframe stuff isn't working, Firefox specific code paths, etc...).

@silas
Copy link
Author

silas commented Mar 7, 2023

Haha, yeah, so when I installed https://addons.mozilla.org/en-US/firefox/addon/uaswitcher/ and just changed my user agent to Chrome it worked fine... 😢

So definitely some Firefox specific code path.

@prameshj
Copy link
Contributor

Interesting.. thanks for sharing this.

There isn't any user agent specific code in the /auth/handler.

  1. Popup window redirects to /__/auth/handler
  2. Popup window immediately closes after redirect

Can you share the full URL to /__/auth/handler in the 2 cases? Thanks!

@silas
Copy link
Author

silas commented Mar 11, 2023

Can you share the full URL to /__/auth/handler in the 2 cases? Thanks!

See below:

  1. Popup window redirects to /__/auth/handler
https://{APP_NAME}.firebaseapp.com/__/auth/handler?apiKey={API_KEY}&appName=%5BDEFAULT%5D&authType=signInViaPopup&redirectUrl=https%3A%2F%2F{APP_NAME}.firebaseapp.com%2F%3F&v=9.15.0&eventId=8661282041&providerId=google.com&customParameters=%7B%22prompt%22%3A%22select_account%22%7D&scopes=profile
  1. Popup window immediately closes after redirect
https://{APP_NAME}.firebaseapp.com/__/auth/handler?state={STATE}&code={CODE}&scope=email+profile+openid+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile&authuser=0&prompt=none

@silas
Copy link
Author

silas commented Mar 12, 2023

The window is definitely getting closed in handler.js. You can use Chrome to fake a Firefox user-agent and add a Window.close breakpoint see this.

  1. Install User-Agent Switcher
  2. Create a using the following settings:
    • New User-agent name: Mac Firefox 110
    • New User-Agent String: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/110.0
    • Group: Firefox
    • Append?: Replace
    • Indicator Flag: FFM
  3. Switch to Mac Firefox 110 user agent
  4. Go through provided POC
  5. When the popup window appears to select the Google account, open the Developer tools
  6. Click Sources -> Event Listener Breakpoints -> Window -> Window.close
  7. Select the Google account and see the section of code that closes the window in handler.js

Someone with access to the source map should then be able to fairly quickly figure out what's going on.

@prameshj
Copy link
Contributor

Thanks for the detailed analysis Silas! I wasn't able to go through the steps you provided, but I looked through the code and yes, we have some legacy SDK codepaths where the popup is closed by auth/handler and there is special case for Firefox. I believe this was because Firefox does not provide the opener with permissions to close the popup (or was the case few versions back).

So, the auth/handler sends an AuthEvent and closes the popup. Meanwhile the main window, listens for AuthEvent and also polls for user cancellation in -

if (this.authWindow?.window?.closed) {
, so if the poller fires before the AuthEvent is processed, then we can end up in error. Perhaps this is exacerbated when there is a delay from blocking functions login. We can fix this by adding a longer delay in the poll function or cancelling the poll once we receive an AuthEvent. I will explore this some more. Thanks again for your detailed analysis here!

@prameshj
Copy link
Contributor

prameshj commented Mar 20, 2023

In the above code, once the poller detects that the window has been closed (will be the case for Firefox), it waits for 2s before throwing error -

. But if the sign in takes longer (due to blocking functions or otherwise), we will end up in this scenario. Changing this timeout to 8s should be sufficient. Sent out #7140, tested with a blocking function that sleeps for 7s.

@silas
Copy link
Author

silas commented May 1, 2023

The fix went out in [email protected] and seems to have resolved the issue, so I'm going to close this issue. Thanks @prameshj for working through it!

@silas silas closed this as completed May 1, 2023
@prameshj
Copy link
Contributor

prameshj commented May 1, 2023

Thank you so much for confirming this and for your detailed investigation, @silas !

@firebase firebase locked and limited conversation to collaborators Jun 1, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

6 participants