Skip to content
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

Initialization takes 30 seconds with the error "Uncaught TypeError: u[v] is not a function" in Chrome for mobile devices #711

Open
yosipy opened this issue Jan 29, 2024 · 7 comments

Comments

@yosipy
Copy link

yosipy commented Jan 29, 2024

Describe the bug

Initialization may take 30 seconds with the error "Uncaught TypeError: u[v] is not a function".
It doesn't happen every time, but at least once every few times.
I have confirmed that this issue occurs on Chrome for mobile devices such as Android devices. (I also reproduced it with the emulator of Chrome's developer tools for PC.) This did not occur on Chrome for PC.

I have created three examples to illustrate this problem and they are shown below. Also, the repository is https://github.com/yosipy/yosi_sandbox/tree/main/20240128_next_firebase_auth_example .

Example using onAuthStateChanged

https://github.com/yosipy/yosi_sandbox/blob/main/20240128_next_firebase_auth_example/pages/sample_on_auth_state_changed.tsx

  1. Start up the example Next.js app server in this repository.
  2. Add the following code to pages/sample_on_auth_state_changed.tsx.
import { FC, useEffect } from "react";

import { useUser, withUser } from "next-firebase-auth";
import { getAuth } from "firebase/auth";

const Index: FC = () => {
  const currenUser = useUser();
  console.log(
    `currenUser.clientInitialized: ${
      currenUser.clientInitialized
    } (${new Date()})`
  );

  const auth = getAuth();
  useEffect(() => {
    const unsubscribeOnAuthStateChanged = auth.onAuthStateChanged(
      (onAuthStateChangedUser) => {
        console.log(
          `onAuthStateChangedUser: ${onAuthStateChangedUser} (${new Date()})`
        );
      },
      (error) => {
        console.error("useFirebaseAuth:", error);
      }
    );

    return () => {
      unsubscribeOnAuthStateChanged();
    };
  }, []);

  return (
    <>
      <p>onAuthStateChanged</p>
    </>
  );
};

export default withUser()(Index);
  1. Open your Chrome browser and use developer tools to view it as a mobile site (Ctrl + Shift + M).
    This icon → image
  2. Visit the page several times. I think it's better to run it in incognito mode to reduce the impact of browser-side cache. I think super reload is better (Ctrl + Shift + R).

Below is the log when the problem occurred.

Download the React DevTools for a better development experience: https://reactjs.org/link/react-devtools
initAuth.js:24 next-firebase-auth [init] Setting config with provided value: {debug: true, loginAPIEndpoint: '/api/login', logoutAPIEndpoint: '/api/logout', authPageURL: ƒ, appPageURL: ƒ, …}
initAuth.js:24 next-firebase-auth [init] Initialized the Firebase JS SDK.
sample_on_auth_state_changed.tsx:39 next-firebase-auth [withUser] Calling "withUser".
websocket.js:48 [HMR] connected
index.browser.js:2 next-firebase-auth [withUser] Set user to: {id: null, email: null, emailVerified: false, tenantId: null, phoneNumber: null, …}
sample_on_auth_state_changed.tsx:8 currenUser.clientInitialized: false (Mon Jan 29 2024 10:41:51 GMT+0900 (日本標準時))
api.js?onload=__iframefcb590529:29 Uncaught TypeError: u[v] is not a function
    at Q.<computed> [as loaded_0] (api.js?onload=__iframefcb590529:29:145)
    at cb=gapi.loaded_0?le=scs:1:6
Q.<computed> @ api.js?onload=__iframefcb590529:29
(anonymous) @ cb=gapi.loaded_0?le=scs:1
index.browser.js:2 next-firebase-auth [withUser] The Firebase ID token changed. New Firebase user: null
index.browser.js:2 next-firebase-auth [withUser] Calling the logout endpoint.
index.browser.js:2 next-firebase-auth [withUser] Set user to: {id: null, email: null, emailVerified: false, tenantId: null, phoneNumber: null, …}
sample_on_auth_state_changed.tsx:8 currenUser.clientInitialized: true (Mon Jan 29 2024 10:41:51 GMT+0900 (日本標準時))
index.browser.js:2 next-firebase-auth [withUser] Completed the auth API request.
index.browser.js:2 next-firebase-auth [withUser] Set user to: {id: null, email: null, emailVerified: false, tenantId: null, phoneNumber: null, …}
sample_on_auth_state_changed.tsx:8 currenUser.clientInitialized: true (Mon Jan 29 2024 10:41:51 GMT+0900 (日本標準時))
sample_on_auth_state_changed.tsx:18 onAuthStateChangedUser: null (Mon Jan 29 2024 10:42:21 GMT+0900 (日本標準時))

The access occurred at approximately 10:41:51, but the onAuthStateChangedUser callback function was executed at 10:42:21.
It took about 30 seconds.

Example sign-in page

https://github.com/yosipy/yosi_sandbox/blob/main/20240128_next_firebase_auth_example/pages/sample_auth.tsx

  1. Start up the example Next.js app server in this repository.
  2. Add the following code to pages/sample_auth.tsx.
import React from "react";
import { withUser } from "next-firebase-auth";
import FirebaseAuth from "../components/FirebaseAuth";

const styles = {
  content: {
    padding: `8px 32px`,
  },
  textContainer: {
    display: "flex",
    justifyContent: "center",
    margin: 16,
  },
};

const Auth = () => (
  <div style={styles.content}>
    <h3>Sign in</h3>
    <div style={styles.textContainer}>
      <p>
        This auth page is <b>static</b>. It will redirect on the client side if
        the user is already authenticated.
      </p>
    </div>
    <div>
      <FirebaseAuth />
    </div>
  </div>
);

export default withUser()(Auth);

This is a modification of pages/auth.js.
3. Modify signInOptions in components/FirebaseAuth.js to allow signing in with a Google account.

  signInOptions: [
    {
      provider: GoogleAuthProvider.PROVIDER_ID,
      customParameters: {
        prompt: "select_account",
      },
    },
  ],
  1. Open your Chrome browser and use developer tools to view it as a mobile site (Ctrl + Shift + M).
    This icon → image
  2. Visit the page several times. I think it's better to run it in incognito mode to reduce the impact of browser-side cache. I think super reload is better (Ctrl + Shift + R).

It will take about 30 seconds for the button that says Sign in with Google to appear.

image →Translation 30 seconds later→ image

Example of retrieving data from Firestore

https://github.com/yosipy/yosi_sandbox/blob/main/20240128_next_firebase_auth_example/pages/sample_firestore1.tsx

  1. Start up the example Next.js app server in this repository.
  2. Add the following code to pages/sample_firestore1.tsx.
import { FC, useEffect, useState } from "react";

import {
  collection,
  getDocs,
  getFirestore,
  limit,
  orderBy,
  query,
  where,
} from "firebase/firestore";
import { withUser } from "next-firebase-auth";
import { getAuth } from "firebase/auth";

const Index: FC = () => {
  const [articleIds, setArticleIds] = useState<string[]>([]);

  const db = getFirestore();

  const fetch = async () => {
    console.log("Fetch start: " + new Date());

    const articlesSnap = await getDocs(
      query(
        collection(db, "articles"),
        where("public", "==", true),
        orderBy("createdAt", "desc"),
        limit(20)
      )
    );
    const fetchedArticleIds = articlesSnap.docs.map((doc) => doc.id);
    setArticleIds(fetchedArticleIds);

    console.log("Fetch end: " + new Date());
  };

  const auth = getAuth();
  useEffect(() => {
    fetch();
  }, []);

  return (
    <>
      <p>articleIds</p>
      {articleIds.map((id) => (
        <div key={id}>{id}</div>
      ))}
    </>
  );
};

export default withUser()(Index);
  1. Change Firestore's security rules to allow data to be retrieved from the articles collection.
  2. Open your Chrome browser and use developer tools to view it as a mobile site (Ctrl + Shift + M).
    This icon → image
  3. Visit the page several times. I think it's better to run it in incognito mode to reduce the impact of browser-side cache. I think super reload is better (Ctrl + Shift + R).

The log will look like this:

Download the React DevTools for a better development experience: https://reactjs.org/link/react-devtools
initAuth.js:24 next-firebase-auth [init] Setting config with provided value: {debug: true, loginAPIEndpoint: '/api/login', logoutAPIEndpoint: '/api/logout', authPageURL: ƒ, appPageURL: ƒ, …}
initAuth.js:24 next-firebase-auth [init] Initialized the Firebase JS SDK.
sample_firestore1.tsx:52 next-firebase-auth [withUser] Calling "withUser".
websocket.js:48 [HMR] connected
index.browser.js:2 next-firebase-auth [withUser] Set user to: {id: null, email: null, emailVerified: false, tenantId: null, phoneNumber: null, …}
sample_firestore1.tsx:21 Fetch start: Mon Jan 29 2024 10:43:44 GMT+0900 (日本標準時)
api.js?onload=__iframefcb846299:29 Uncaught TypeError: u[v] is not a function
    at Q.<computed> [as loaded_0] (api.js?onload=__iframefcb846299:29:145)
    at cb=gapi.loaded_0?le=scs:1:6
Q.<computed> @ api.js?onload=__iframefcb846299:29
(anonymous) @ cb=gapi.loaded_0?le=scs:1
index.browser.js:2 next-firebase-auth [withUser] The Firebase ID token changed. New Firebase user: null
index.browser.js:2 next-firebase-auth [withUser] Calling the logout endpoint.
index.browser.js:2 next-firebase-auth [withUser] Set user to: {id: null, email: null, emailVerified: false, tenantId: null, phoneNumber: null, …}
index.browser.js:2 next-firebase-auth [withUser] Completed the auth API request.
index.browser.js:2 next-firebase-auth [withUser] Set user to: {id: null, email: null, emailVerified: false, tenantId: null, phoneNumber: null, …}
sample_firestore1.tsx:34 Fetch end: Mon Jan 29 2024 10:44:15 GMT+0900 (日本標準時)

It takes about 30 seconds to execute the fetch function (10:43:44 ~ 10:44:15).
Requests and responses to Firestore take very little time; the steps before that take time.
image

Putting { whenUnauthedBeforeInit: AuthAction.RETURN_NULL } in the argument of withUser seems to solve the problem in this repository example. However, it was not a solution for my other application (I could not reproduce it using the example from this repository)

Versions

next-firebase-auth version: "1.0.2"
Firebase JS SDK (firebase): "9.17.1"
Firebase admin SDK (firebase-admin): "^11.9.0"
Next.js: "13.4.9"

To Reproduce
I wrote it in Describe the bug

Expected behavior
I wrote it in Describe the bug

Debug and error logs
Please provide debug logs or errors from onVerifyTokenError and onTokenRefreshError.

Additional context
Add any other context about the problem here.

@yosipy
Copy link
Author

yosipy commented Jan 29, 2024

Putting { whenUnauthedBeforeInit: AuthAction.RETURN_NULL } in the argument of withUser seems to solve the problem in this repository example. However, it was not a solution for my other application (I could not reproduce it using the example from this repository)

I noticed that even if I added { whenUnauthedBeforeInit: AuthAction.RETURN_NULL } to the three examples above, the issue still reproduced when I connected to the emulator.

Example using onAuthStateChanged(with emulator)

https://github.com/yosipy/yosi_sandbox/blob/main/20240128_next_firebase_auth_example/pages/sample_on_auth_state_changed2.tsx

import { FC, useEffect } from "react";

import { AuthAction, useUser, withUser } from "next-firebase-auth";
import { getAuth } from "firebase/auth";

const Index: FC = () => {
  const currenUser = useUser();
  console.log(
    `currenUser.clientInitialized: ${
      currenUser.clientInitialized
    } (${new Date()})`
  );

  const auth = getAuth();
  useEffect(() => {
    const unsubscribeOnAuthStateChanged = auth.onAuthStateChanged(
      (onAuthStateChangedUser) => {
        console.log(
          `onAuthStateChangedUser: ${onAuthStateChangedUser} (${new Date()})`
        );
      },
      (error) => {
        console.error("useFirebaseAuth:", error);
      }
    );

    return () => {
      unsubscribeOnAuthStateChanged();
    };
  }, []);

  return (
    <>
      <p>onAuthStateChanged</p>
    </>
  );
};

export default withUser({
  whenUnauthedBeforeInit: AuthAction.RETURN_NULL,
})(Index);

Log

Download the React DevTools for a better development experience: https://reactjs.org/link/react-devtools
initAuth.js:20 WARNING: You are using the Auth Emulator, which is intended for local testing only.  Do not use with production credentials.
initAuth.js:24 next-firebase-auth [init] Setting config with provided value: {debug: true, loginAPIEndpoint: '/api/login', logoutAPIEndpoint: '/api/logout', authPageURL: ƒ, appPageURL: ƒ, …}
initAuth.js:24 next-firebase-auth [init] Initialized the Firebase JS SDK.
initAuth.js:24 WARNING: You are using the Auth Emulator, which is intended for local testing only.  Do not use with production credentials.
sample_on_auth_state_changed2.tsx:41 next-firebase-auth [withUser] Calling "withUser".
websocket.js:48 [HMR] connected
index.browser.js:2 next-firebase-auth [withUser] Set user to: {id: null, email: null, emailVerified: false, tenantId: null, phoneNumber: null, …}
api.js?onload=__iframefcb873588:29 Uncaught TypeError: u[v] is not a function
    at Q.<computed> [as loaded_0] (api.js?onload=__iframefcb873588:29:145)
    at cb=gapi.loaded_0?le=scs:1:6
Q.<computed> @ api.js?onload=__iframefcb873588:29
(anonymous) @ cb=gapi.loaded_0?le=scs:1
index.browser.js:2 next-firebase-auth [withUser] The Firebase ID token changed. New Firebase user: null
index.browser.js:2 next-firebase-auth [withUser] Calling the logout endpoint.
index.browser.js:2 next-firebase-auth [withUser] Set user to: {id: null, email: null, emailVerified: false, tenantId: null, phoneNumber: null, …}
index.browser.js:2 next-firebase-auth [withUser] Completed the auth API request.
index.browser.js:2 next-firebase-auth [withUser] Set user to: {id: null, email: null, emailVerified: false, tenantId: null, phoneNumber: null, …}
sample_on_auth_state_changed2.tsx:8 currenUser.clientInitialized: true (Mon Jan 29 2024 12:40:31 GMT+0900 (日本標準時))
sample_on_auth_state_changed2.tsx:18 onAuthStateChangedUser: null (Mon Jan 29 2024 12:41:01 GMT+0900 (日本標準時))

It takes about 30 seconds from 12:40:31 to 12:41:01.

@yosipy
Copy link
Author

yosipy commented Jan 31, 2024

I'm continuing to investigate, focusing on the initialization part, but no progress has been made.

I don't know if the following is related to this problem, and I may be wrong.
I noticed that the FirebaseApp object in my application and the FirebaseApp object in next-firebase-auth are not shared.
The FirebaseApp object that I created in advance cannot be read by initFirebaseClientSDK.ts.

I don't know if this is the intended behavior, but reading the comments below makes me think it's not.
#599 (comment)

I confirmed this by adding log output as shown below.

example code

initAuth.js in my application( https://github.com/yosipy/yosi_sandbox/blob/main/20240201_next_firebase_auth_example_for_get_apps/utils/initAuth.js )

@@ -1,5 +1,5 @@
 /* globals window */
-import { initializeApp } from "firebase/app";
+import { getApps, initializeApp } from "firebase/app";
 import { init } from "next-firebase-auth";
 import absoluteUrl from "next-absolute-url";
 import { getAuth, connectAuthEmulator } from "firebase/auth";
@@ -20,6 +20,9 @@ const initAuth = (useEmulator) => {
     connectAuthEmulator(auth, "http://localhost:9099");
   }

+  console.log(
+    `[my-app: before init({...})] getApps().length: ${getApps().length}`
+  );
   // Initialize next-firebase-auth.
   init({
     debug: true,

src/initFirebaseClientSDK.ts in next-firebase-auth

@@ -6,6 +6,11 @@ import logDebug from 'src/logDebug'
 export default function initFirebaseClientSDK() {
   const { firebaseClientInitConfig, firebaseAuthEmulatorHost, tenantId } =
     getConfig()
+  console.log(
+    `[next-firebase-auth: beggining of initFirebaseClientSDK()] getApps().length: ${
+      getApps().length
+    }`
+  )
   if (!getApps().length) {
     if (!firebaseClientInitConfig) {
       throw new Error(

Run yarn run dev:publish in next-firebase-auth. Run yalc add next-firebase-auth and rm -rf .next/ && npm run dev in my application.
Below is the log. On my application side, getApps().length is 1 before init({...}). However, after that, getApps().length is 0 on the next-firebase-auth side.

Download the React DevTools for a better development experience: https://reactjs.org/link/react-devtools
initAuth.js:23 [my-app: before init({...})] getApps().length: 1
initAuth.js:27 next-firebase-auth [init] Setting config with provided value: {debug: true, loginAPIEndpoint: '/api/login', logoutAPIEndpoint: '/api/logout', authPageURL: ƒ, appPageURL: ƒ, …}
initAuth.js:27 [next-firebase-auth: beggining of initFirebaseClientSDK()] getApps().length: 0
initAuth.js:27 next-firebase-auth [init] Initialized the Firebase JS SDK.
auth.js:35 next-firebase-auth [withUser] Calling "withUser".
websocket.js:48 [HMR] connected
index.browser.js:2 next-firebase-auth [withUser] Set user to: {id: null, email: null, emailVerified: false, tenantId: null, phoneNumber: null, …}
index.browser.js:2 next-firebase-auth [withUser] The Firebase ID token changed. New Firebase user: null
index.browser.js:2 next-firebase-auth [withUser] Calling the logout endpoint.
index.browser.js:2 next-firebase-auth [withUser] Set user to: {id: null, email: null, emailVerified: false, tenantId: null, phoneNumber: null, …}
index.browser.js:2 next-firebase-auth [withUser] Completed the auth API request.
index.browser.js:2 next-firebase-auth [withUser] Set user to: {id: null, email: null, emailVerified: false, tenantId: null, phoneNumber: null, …}

@yosipy
Copy link
Author

yosipy commented Jan 31, 2024

It seems to be the same issue as the issue below.
firebase/firebaseui-web#1053

The direct cause may not be in next-firebase-auth. However, strangely, in my environment, removing the initialization process of next-firebase-auth solves the problem.

@yosipy
Copy link
Author

yosipy commented Feb 8, 2024

The issue was resolved by downgrading Firebase version to 9.16.0.

The solution to the issue below may have another problem.
#614

@jodik
Copy link

jodik commented Mar 13, 2024

Also experiencing this on even outside of mobile - Desktop Safari. Desktop Chrome and Firefox work fine

@maclementED
Copy link

Im also experimenting the same issue. Downgrading firebase did not fixed the issue.

Im still searching, but it's seems that when gapi throws an errors, useFirebaseUser no longer receive the new Firebase ID token for 30 seconds. auth.currentUser?.getIdToken(true) did not helped forcing useFirebaseUser to update.

Note: Took 30sec to receive this log:
image

@evnp
Copy link

evnp commented Sep 24, 2024

We've been fighting with this issue with basic firebase/auth and firebaseui packages (no usage of Next.js or next-firebase-auth whatsoever, but very similar symptoms). We're actually using four different firebase modules, the v8 "modular" import style:

firebase/app
firebase/auth
firebase/database
firebaseui

We've found that this error seems to occur when code from multiple of these imports is evaluated at once, the modules internally load multiple instances of Google's api.js script, and some sort of conflict occurs which raises this u[v] is not a function error from within api.js. For us, this puts FirebaseUI into some kind of crashed state where clicking the buttons does nothing for 30-60s, then it recovers.

The only way we've found to avoid this error and broken state is to dynamically import these four firebase modules, and actually import them serially with a small delay (100-200ms) in between. This seems to prevent any contention of api.js execution and is the only thing we've found that prevents the u[v] is not a function error from ever appearing. We don't need these modules for all users so the dynamic imports have side benefits of reducing our main app bundle size, but I know this may not be true or feasible in all use cases.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants