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

Firebase web 9 Auth incompatible with Capacitor 3 #5019

Closed
riderx opened this issue Jun 12, 2021 · 35 comments
Closed

Firebase web 9 Auth incompatible with Capacitor 3 #5019

riderx opened this issue Jun 12, 2021 · 35 comments

Comments

@riderx
Copy link
Contributor

riderx commented Jun 12, 2021

  • 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()
@sam-gc
Copy link
Contributor

sam-gc commented Jun 15, 2021

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.

@riderx
Copy link
Contributor Author

riderx commented Jun 15, 2021

@sam-gc i don't need social auth so for sure it will helps !
i got the same issue of #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=**** 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
Copy link
Contributor Author

riderx commented Aug 26, 2021

@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

@harry-herskowitz
Copy link

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

@jayordway
Copy link

jayordway commented Sep 17, 2021

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.

@harry-herskowitz
Copy link

@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
Copy link

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

@riderx
Copy link
Contributor Author

riderx commented Sep 21, 2021

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

@jayordway
Copy link

jayordway commented Sep 28, 2021

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.

@mesqueeb
Copy link

mesqueeb commented Oct 5, 2021

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.

@sam-gc
Copy link
Contributor

sam-gc commented Oct 5, 2021

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.

@mesqueeb
Copy link

mesqueeb commented Oct 6, 2021

@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
Copy link

mesqueeb commented Oct 6, 2021

@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 ]]

@floppydisken
Copy link

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.

@sam-gc
Copy link
Contributor

sam-gc commented Oct 7, 2021

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

@riderx
Copy link
Contributor Author

riderx commented Oct 7, 2021

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

@ejirocodes
Copy link

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

@Rampin97
Copy link

Rampin97 commented Nov 9, 2021

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

@tobium
Copy link

tobium commented Nov 12, 2021

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

@jamesonsaunders
Copy link

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;

@wmadden
Copy link

wmadden commented May 3, 2022

Reposting: #5020 (comment)

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" }

@yoyomyo
Copy link

yoyomyo commented May 5, 2022

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

@riderx
Copy link
Contributor Author

riderx commented May 5, 2022

Thanks @wmadden even i have left Firebase for Supabase who is more reactive.
i made a fix from your comment as PR : #6236

@WilliamAnaya
Copy link

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

@riderx
Copy link
Contributor Author

riderx commented May 23, 2022

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

@corysmc
Copy link

corysmc commented Aug 12, 2022

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();
    }
  });
}

@riderx
Copy link
Contributor Author

riderx commented Aug 12, 2022

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

@DeveloperAdam-github
Copy link

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!

@miguelromeroh
Copy link

miguelromeroh commented Aug 24, 2022

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

@DeveloperAdam-github
Copy link

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?

@brian-weasner
Copy link

brian-weasner commented Mar 6, 2023

If using Firebase (auth-compat) with Capacitor and Angular, and following the advice further up in this issues does not help, (ie. changing scheme to ionic:// or intializing auth with indexDBLocalPersistence), then you may have the following issue.

To check if you have my issue:

  1. Load up your application and go into web inspector for your hybrid app.
  2. After your application has loaded, in the console run document.addEventListener('deviceready', (event) => console.log('Device Ready Fired'));
  3. If "Device Ready Fired" is not immediately logged to the console, you probably have the same issue I have.

After debugging it turns out that we have a monkey patch in place to fix a zone.js issue where callback/listeners do not fire within ngZone and therefore do not trigger change detection. See this Github Issue for more information.

If you have added the monkey patch like I have, then your issue is that Capacitor loads native-bridge.ts and cordova.js at the top of the html head element. This means that when your app initializes, native-bridge.ts and cordova.js is run before the monkeypatch. See Capacitor github issue filed here

Long story short, event listeners get messed up and deviceready fires after the device is initially ready, but doesn't fire immediately on subsequent additions of listners. Which is what should happen according to the the cordova docs here.

Due to device ready only firing the first time, the firebase compatibility layer that initializes firebase auth fails to use the correct "resolver" and loads iframe.js when it shouldn't. See _isCordova function.

My Fix

I've updated my monkeypatch to include the same logic that is within Capacitor's native-bridge.ts, and moved my monkeypatch from my main.ts to the head in index.html

document.addEventListener = function () {
	// Duplicating functionality from Capacitor.js native-bridge.ts, otherwise when cordova.js calls addEventListener for 'deviceready' will not immediately fire if 'deviceready' has already emitted.
	// Need to duplicate capacitor logic because capacitor injects native-bridge.ts code at the top of the head, so this function overwrites it.
	// @see https://github.com/ionic-team/capacitor/blob/89cddcd6497034146e0938ce8c264e22e7baba52/core/native-bridge.ts#L152
	const [eventName, handler] = arguments;
	if (eventName === 'deviceready' && handler) {
		Promise.resolve().then(handler);
	}
	// We don't care about back button error checks or default 'backbutton' action as we have @capacitor/app plugin installed, and set a listener within app.component.ts
	(window.EventTarget || Document).prototype.addEventListener.apply(this, arguments);
};

@DellaBitta
Copy link
Contributor

Checking in to see if there's anything for the Firebase JavaScript SDK to do for this issue still. Thanks!

@google-oss-bot
Copy link
Contributor

Hey @riderx. We need more information to resolve this issue but there hasn't been an update in 5 weekdays. I'm marking the issue as stale and if there are no new updates in the next 5 days I will close it automatically.

If you have more information that will help us get to the bottom of this, just add a comment!

@riderx
Copy link
Contributor Author

riderx commented Jun 13, 2023

Sorry bot i cannot help much, i have stop using firebase, I switched to Supabase who works without need of tricks.

@DellaBitta
Copy link
Contributor

Ok, thanks. I'm going to close this issue for now. I know there are other participants here. If you're still having problems I'd ask for you to create a new issue with all of your environment / framework information, and to link to this issue in your report. Thanks!

@firebase firebase locked and limited conversation to collaborators Jul 14, 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