firebase-js-sdk icon indicating copy to clipboard operation
firebase-js-sdk copied to clipboard

Firebase web 9 Auth incompatible with Capacitor 3

Open riderx opened this issue 3 years ago • 30 comments

  • Operating System version: ios 14.5
  • Browser version: Capacitor
  • Firebase SDK version: 9 beta 2
  • Firebase Product: auth

[REQUIRED] Describe the problem

When getting the auth with getAuth(firebaseApp); The app crash and the only issue I see in safari dev console is :

TypeError: undefined is not an object (evaluating 'gapi.iframes.getContext')

Relevant Code:

import { getAuth, onAuthStateChanged, signInAnonymously } from "firebase/auth";
import { getFirestore } from "firebase/firestore";
import { initializeApp } from "firebase/app"

var firebaseConfig = {
    apiKey: "***",
    authDomain: "***",
    databaseURL: "***",
    projectId: "***",
    storageBucket: "***",
    messagingSenderId: "****",
    appId: "***"
};
const firebaseApp = initializeApp(firebaseConfig);

export const user = new Promise((resolve, reject) => {
    try {    
        const auth = getAuth(firebaseApp);
        const removeListener = onAuthStateChanged(auth, user => {
            // Check for user status
            if (!user) {
                signInAnonymously(auth);
                return
            }
            console.log('user', user); // Will log the user object.
            removeListener();
            resolve(user);
        });
    } catch (err) {
        console.error('reject', err);
        reject(err)
    }
})
user()

riderx avatar Jun 12 '21 11:06 riderx

Hi @riderx -- we don't explicitly support Capacitor, nor is it on our roadmap at this time. With that said, you're running into this issue because the default getAuth() function assumes you're in a browser context and automatically includes pieces you need to perform social login (i.e. signInWithPopup and signInWithRedirect). Instead, I'd suggest trying to initialize auth manually, which allows you to specifically choose your dependencies. For example:

const auth = initializeAuth(app, {
  persistence: browserLocalPersistence,  // This uses localStorage
});

Here is the reference docs: https://firebase.google.com/docs/reference/js/v9/auth#initializeauth

I'm not sure whether or not this will work with Capacitor, but it should at least get you past the error you linked.

sam-gc avatar Jun 15 '21 19:06 sam-gc

@sam-gc i don't need social auth so for sure it will helps ! i got the same issue of https://github.com/firebase/firebase-js-sdk/issues/5020 now

[Log] onscript loading complete (user-script:2, line 262)
[Error] Preflight response is not successful
[Error] Fetch API cannot load https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=AIzaSyA38ipaW0j7sY34I3DiHL3AykwNNJ94aGA due to access control checks.
[Error] Failed to load resource: Preflight response is not successful (accounts:signUp, line 0)
[Warning] Ignoring Event: localhost (user-script:2, line 262)
[Error] Unhandled Promise Rejection: FirebaseError: Firebase: Error (auth/network-request-failed).
	dispatchException (4.9789fcb5.chunk.js:2:798381)
	(anonymous function) (4.9789fcb5.chunk.js:2:794609)
	r (4.9789fcb5.chunk.js:2:188)
	u (4.9789fcb5.chunk.js:2:428)
	promiseReactionJob

What I don't get is I have 2 apps in store (Captime and Mimesis) on google play store and apple who worked well until few days ago, now none of them work :/

riderx avatar Jun 15 '21 23:06 riderx

@sam-gc i'm still unable to use firebase 9 in my apps, do you if it will be fixed, and when ?

I have to know if I need to switch from firebase to something else for my mobile apps

riderx avatar Aug 26 '21 14:08 riderx

@riderx what did you end up doing? Looks like I'll need to ditch firebase as well

harry-herskowitz avatar Sep 17 '21 16:09 harry-herskowitz

I have tried both firebase 8 & 9 now. The issue exists with Capacitor 3 and iOS 14, I have not tested other versions or on Android. I don't know the root cause yet, I am sure that in the worse case you could use your own api/server and do the firebase authentication there. I truly hope it is not something related to the WebKit.

jayordway avatar Sep 17 '21 17:09 jayordway

@jayordway let's hope this gets resolved :/ I'm sure there's a lot of people stuck on this. Thank you for your insight.

harry-herskowitz avatar Sep 17 '21 17:09 harry-herskowitz

FIXED! Here's the solution: https://harryherskowitz.com/2021/08/23/firebase-capacitor.html

harry-herskowitz avatar Sep 21 '21 14:09 harry-herskowitz

@roldyclark thanks a lot, i will try to upgrade to v9 so :)

riderx avatar Sep 21 '21 22:09 riderx

This worked for me, but it is work elaborating that I was not able to get it to work with angularfire. I had to revert to using firebase 9 directly.

 init() {
    const app = initializeApp(environment.firebase);
    if (Capacitor.isNativePlatform) {
      initializeAuth(app, {
        persistence: indexedDBLocalPersistence
      });
    }
    this.firestore = getFirestore(app);
  }

The reason I did this is because I could not find a reliable way to set the persistence properly with "indexedDBLocalPersistence" when using angularFire. It may be possible though, I will attempt once more and report back.

jayordway avatar Sep 28 '21 06:09 jayordway

Ever since I started using Capacitor 3.x with Firebase 9.x compat build I cannot launch Capacitor anymore with the same error as the OP.

@sam-gc With the compat build however there is no concept of const auth = initializeAuth(app, ... because it's the compat syntax.

Does anyone know how I can solve this issue without having to convert to the modular syntax? The reason is that some plugins I use do not yet support the modular syntax.

mesqueeb avatar Oct 05 '21 06:10 mesqueeb

For the folks using v9 modular, I would also suggest trying to import getAuth from firebase/auth/cordova. This will also avoid the browserPopupRedirectResolver dependency.

@mesqueeb, I will investigate. The compat layer should select the correct dependency at runtime.

sam-gc avatar Oct 05 '21 16:10 sam-gc

@sam-gc Thank you very much. I spent all day yesterday upgrading all dependencies to V9 Modular syntax, and now my app launches. However, now I'm getting this error on Capacitor:

Failed to load resource: the server responded with a status of 404 (Not Found)

The error has to do with Firestore not being able to connect in my Capacitor app. It thinks it has no connection, but my phone definitely has internet connection.

The web version in the browser loads normally.

PS: I already use modular Firestore SDK syntax as well

Potential causes:

  1. Maybe some Capacitor setting that prevents internet connection? But the Capacitor browser plugin works fine.
  2. Maybe some Firebase SDK issue?

Should I open a new issue for this?

See attached a screenshot of some error logs: Screenshot 2021-10-06 at 13 08 05

I definitely checked internet connection both on wifi and 4G and the phone is connected properly to the internet. The capacitor in-app browser also works. Just Firestore won't connect.

mesqueeb avatar Oct 06 '21 02:10 mesqueeb

@sam-gc Update: It works when I instantiate firestore like so:

const firestore =
    platform === 'web'
      ? getFirestore(firebaseApp)
      : initializeFirestore(firebaseApp, { experimentalForceLongPolling: true })

Do you think we always will need to "ForceLongPolling" for Capacitor apps? And are there any downsides of doing this?

[[ PS: Let me know if I need to move these comments into a new issue ]]

mesqueeb avatar Oct 06 '21 04:10 mesqueeb

FIXED! Here's the solution: https://harryherskowitz.com/2021/08/23/firebase-capacitor.html

I was facing an internal uncatchable auth-network-error on iOS but not on Android. This fixed it.

I am using Capacitor 3 as well.

floppydisken avatar Oct 06 '21 22:10 floppydisken

@mesqueeb, yes please create a separate issue for the Firestore issue you're experiencing.

sam-gc avatar Oct 07 '21 16:10 sam-gc

I will move all my apps to supabase, firebase is driving me crazy for a too long time

riderx avatar Oct 07 '21 22:10 riderx

Fixed here: https://stackoverflow.com/a/69793220/12624390

ejirocodes avatar Nov 01 '21 08:11 ejirocodes

Any solutions for non-modular syntax? I'm using AngularFire and I have the same problem (iOS only, Android works fine)

Rampin97 avatar Nov 09 '21 13:11 Rampin97

me too. AngularFire doesn't work with Capacitor on iOS :(

tobium avatar Nov 12 '21 13:11 tobium

The real problem: firebase-js-sdk on mobile iOS assumes google API (gapi) exists on the window, even when it isn't used.

I found a work around: Mock window.gapi before using firebase auth login:

window['gapi'] = {
  load: (name: string) => Promise.resolve(),
  iframes: {
    getContext: () => {
      return {
        iframe: {
          contentWindow: {
            postMessage: (message: any) => {
              console.log("gapi iframe message:", message);
            }
          }
        }
      }
    }
  }
} as any;

jamesonsaunders avatar Feb 04 '22 17:02 jamesonsaunders

Reposting: https://github.com/firebase/firebase-js-sdk/issues/5020#issuecomment-1116237515

It looks like the issue is Firebase incorrectly detecting the environment (see @alistairheath's comment here).

There's a check in the auth/compat library here which checks for the URL scheme "ionic://", which was used by older versions of Capacitor (it's now "capacitor://") and if you set it back to "ionic://" (see the config option iosScheme) it seems to work again and doesn't attempt to load the gapi libraries.

TL;DR, in capacitor.config set server: { iosScheme: "ionic" }

wmadden avatar May 03 '22 15:05 wmadden

This entry contains multiple issues, including one fixed. Filed an umbrella bug to track.

yoyomyo avatar May 05 '22 21:05 yoyomyo

Thanks @wmadden even i have left Firebase for Supabase who is more reactive. i made a fix from your comment as PR : https://github.com/firebase/firebase-js-sdk/pull/6236

riderx avatar May 05 '22 23:05 riderx

in capacitor.config set server: { iosScheme: "ionic" }

WilliamAnaya avatar May 20 '22 01:05 WilliamAnaya

The Pr is merged ! It will come soon in production !

riderx avatar May 23 '22 19:05 riderx

Seeing as this issue is still open, I wanted to confirm I'm doing this correctly still, and I noticed others have recently reported recently as well: https://github.com/firebase/firebase-js-sdk/issues/6504

On the latest version of firebase (9.9.2) I did not have to change the iosScheme to "ionic" - looks like that issue was already resolved.

I got it working with this per @jayordway 's suggestion (this is the only thing I changed to get the native iOS build working as web was working fine).

Create a new function to get firebase auth

let auth: Auth;
export function getFirebaseAuth() {
  if (auth) return auth;
  if (Capacitor.isNativePlatform()) {
    auth = initializeAuth(getApp(), { persistence: indexedDBLocalPersistence });
  } else {
    auth = getAuth();
  }
  return auth;
}

Then anywhere I would normally call getAuth() I use the new function:

e.g. when Initializing firebase

export function initFirebase() {
  initializeApp(environment.firebaseConfig);
  initializeFirestore(getApp(), { ignoreUndefinedProperties: true });
  getFirebaseAuth().onAuthStateChanged(user => {
    if (user) {
      signIn(user);
    } else {
      signOut();
    }
  });
}

corysmc avatar Aug 12 '22 15:08 corysmc

Yes the key is the persistence you must do it only on native

riderx avatar Aug 12 '22 18:08 riderx

I am running Vue 3, Pinia, Firebase 9, Cap 4, can get registering all good, signing in all good with email and google using some plugins. But ANYTHING I do to try and either getDoc() or setDoc() it just doesn't seem to work but works totally fine on web.

I am using the fixes above and still nothing, anyone else had this - driving me mad?

Thanks!

DeveloperAdam-github avatar Aug 23 '22 21:08 DeveloperAdam-github

Seeing as this issue is still open, I wanted to confirm I'm doing this correctly still, and I noticed others have recently reported recently as well: #6504

On the latest version of firebase (9.9.2) I did not have to change the iosScheme to "ionic" - looks like that issue was already resolved.

I got it working with this per @jayordway 's suggestion (this is the only thing I changed to get the native iOS build working as web was working fine).

Create a new function to get firebase auth

let auth: Auth;
export function getFirebaseAuth() {
  if (auth) return auth;
  if (Capacitor.isNativePlatform()) {
    auth = initializeAuth(getApp(), { persistence: indexedDBLocalPersistence });
  } else {
    auth = getAuth();
  }
  return auth;
}

Then anywhere I would normally call getAuth() I use the new function:

e.g. when Initializing firebase

export function initFirebase() {
  initializeApp(environment.firebaseConfig);
  initializeFirestore(getApp(), { ignoreUndefinedProperties: true });
  getFirebaseAuth().onAuthStateChanged(user => {
    if (user) {
      signIn(user);
    } else {
      signOut();
    }
  });
}
image

For Ionic 6 this is valid. Resolves the cors issue even without the mentioned scheme change for CapacitorConfig, or backend change to http native

miguelromeroh avatar Aug 24 '22 19:08 miguelromeroh

When I use the

if (Capacitor.isNativePlatform()) { auth = initializeAuth(getApp(), { persistence: indexedDBLocalPersistence }); } else { auth = getAuth(); } return auth;

The auth on Capacitor looks totally different compared to web, in fact it has hardly any data so I am not able to get the currentUser etc.

If you log your auth on web vs capacitor are they the same?

DeveloperAdam-github avatar Aug 24 '22 20:08 DeveloperAdam-github