capacitor-firebase icon indicating copy to clipboard operation
capacitor-firebase copied to clipboard

bug: authStateChange never fires.

Open braincomb opened this issue 3 years ago • 30 comments

Plugin(s): "@capacitor-firebase/authentication": "^0.5.0"

Platform(s): Android

Current behavior: When using const result = await FirebaseAuthentication.signInWithGoogle() for example, the login is successful and result.user is returned. However, authStateChange is never fired. Note that this works in web, but NOT on Android.

Expected behavior: authStateChange needs to be fired on any change in auth state, e.g. re-opening app or signing out.

Steps to reproduce:

Related code:

  // capacitor.config.json
  "plugins": {
    "FirebaseAuthentication": {
      "skipNativeAuth": false,
      "providers": ["twitter.com", "google.com", "facebook.com"]
    },
  }
// main.ts
import { FirebaseAuthentication } from "@capacitor-firebase/authentication";
FirebaseAuthentication.removeAllListeners().then(() => {
  FirebaseAuthentication.addListener("authStateChange", (result) => {
    if (result.user) {
      // this never fires
    } else {
      // this never fires
    }
  });
});
  // Login.vue
  const signInWithGoogle = async () => {
    try {
      const result: SignInResult = await FirebaseAuthentication.signInWithGoogle();

      if (result.user) {
        return result.user;
      }
    } catch (err) {
      console.log(err);
    }
  };

Other information: Some potentially relevant logs from Android Studio:

V/Capacitor/FirebaseAuthenticationPlugin: Notifying listeners for event authStateChange D/Capacitor/FirebaseAuthenticationPlugin: No listeners found for event authStateChange V/Capacitor/Plugin: To native (Capacitor plugin): callbackId: 81763094, pluginId: FirebaseAuthentication, methodName: addListener V/Capacitor: callback: 81763094, pluginId: FirebaseAuthentication, methodName: addListener, methodData: {"eventName":"authStateChange"}

Also, it looks like there's a similar issue that was closed but with no real solution, https://github.com/capawesome-team/capacitor-firebase/issues/56

Capacitor doctor:

Latest Dependencies:

  @capacitor/cli: 3.6.0
  @capacitor/core: 3.6.0
  @capacitor/android: 3.6.0
  @capacitor/ios: 3.6.0

Installed Dependencies:

  @capacitor/cli: 3.4.3
  @capacitor/core: 3.4.3
  @capacitor/android: 3.4.3
  @capacitor/ios: 3.4.3

braincomb avatar Jul 12 '22 20:07 braincomb

Hi @braincomb, the problem with #56 was that i could not reproduce it. In my demo app the listener works. Could you try the demo app and see if it works for you?

robingenz avatar Jul 13 '22 06:07 robingenz

Hi @robingenz,

I'll try the demo app when I get a chance, but so far the only difference that I can see is that you do not initiliazeApp firebase app if the platform is native Capacitor. I don't know if that makes any difference, but I do require to initialize JS SDK, since I'm also using Firestore.

Additionally, I noticed that Notifying listeners for event authStateChange happens before the listener is added in the Android Studio logs. I don't know if this has any significance.

In the end I think I will end up having to use skipNativeAuth=true with JS SDK auth, since that also gives me AdditionalUserInfo that I need.

Thank you.

braincomb avatar Jul 13 '22 19:07 braincomb

skipNativeAuth should not matter in this case. I have tested it with both.

robingenz avatar Jul 14 '22 05:07 robingenz

same problem here but with iOS, it works on the Web

aronsuarez avatar Jul 15 '22 09:07 aronsuarez

I too am having the same issue. Vue App. Calling the addListener and it is attaching it. It just never gets fired on IOS. Any ideas?

jaythegeek avatar Jul 24 '22 01:07 jaythegeek

Please provide a minimal, reproducible example so that I can debug the it.

robingenz avatar Jul 24 '22 06:07 robingenz

I have been stumped by this same issue all day and was forced to create a workaround, it works correctly when running in the browser but not on the device. In the browser, the listener is called at startup, on device it is not.

this work around fails because I don't have access to the js authenticated user when restarting the app with a previously logged in user

    await FirebaseAuthentication.removeAllListeners();

    if (Capacitor.isNativePlatform()) {
        const result = await FirebaseAuthentication.getCurrentUser();

        //  This always returns null, even if the result is not null
        console.log("js user", await getAuth().currentUser);

        callback(result?.user);
        return
    }

    // this never fires on the device
    FirebaseAuthentication.addListener("authStateChange", (result) => {
        if (result.user) {

            //  This works correctly in browser
            console.log("js user", await getAuth().currentUser);

            callback(result.user);
            debugger;
        } else {
            callback(null);
        }
    });

aaronksaunders avatar Jul 25 '22 20:07 aaronksaunders

Hi @braincomb, i think i got your problem now. Does this help you: https://github.com/capawesome-team/capacitor-firebase/issues/159#issuecomment-1194667877 ?

robingenz avatar Jul 25 '22 22:07 robingenz

@robingenz - sample project in vuejs where the listener is not firing when app starts up on device

https://github.com/aaronksaunders/mobile-gauth

aaronksaunders avatar Jul 25 '22 22:07 aaronksaunders

@aaronksaunders Thank you! I will have a look.

robingenz avatar Jul 26 '22 11:07 robingenz

@aaronksaunders Have you tested your demo? It works on the Web but not on iOS, i just get a blank white screen without error message on xcode or safari devtools: grafik

robingenz avatar Aug 01 '22 10:08 robingenz

@robingenz that is the bug... the state change is never firing at startup so the app doesn't now if it should show the home screen or the login screen

aaronksaunders avatar Aug 01 '22 14:08 aaronksaunders

@aaronksaunders The auth state change listener is firing. See my screenshot. You can debug it with XCode yourself. grafik

Maybe it is a problem with Capacitor or your code. I'll try to find out more. In any case, the plugin is doing its job.

robingenz avatar Aug 02 '22 15:08 robingenz

@robingenz - I debugged it in your code also, the problem is that it is firing before the Ionic app is completely initialized and able to set the listener up.

just because it is firing in the plugin doesn't demonstrate that it is actually properly notifying its listeners...

do you have a sample application showing that in fact listeners sent in code are being called in the Ionic application? I will be happy to follow that example but what you have shown here doesn't validate that listeners are 1) being registered properly on the device and two are actually being called

aaronksaunders avatar Aug 02 '22 16:08 aaronksaunders

@aaronksaunders thank you for taking the time to set up a reproducible project; I am of the same assessment that the event may be firing from the plugin but it either 1) doesn't propagate to the WebView layer, or 2) as you said fires before the listener is set up. This can be observed from the logs in my original post from Android Studio:

V/Capacitor/FirebaseAuthenticationPlugin: Notifying listeners for event authStateChange
D/Capacitor/FirebaseAuthenticationPlugin: No listeners found for event authStateChange
V/Capacitor/Plugin: To native (Capacitor plugin): callbackId: 81763094, pluginId: FirebaseAuthentication, methodName: addListener
V/Capacitor: callback: 81763094, pluginId: FirebaseAuthentication, methodName: addListener, methodData: {"eventName":"authStateChange"}

braincomb avatar Aug 02 '22 17:08 braincomb

@braincomb thanks for doing the work also, I was just about to do the same with the IOS plugin, but it already confirms my suspicion that the authstate change is firing at startup of the plugin and the web code hasn't attached the listener yet. Hopefully, this will be enough proof for @robingenz to take another look at this.

aaronksaunders avatar Aug 02 '22 17:08 aaronksaunders

@aaronksaunders did you come up with any temporary solution? I'm experiencing the exact same problem.

leochaddad avatar Aug 02 '22 17:08 leochaddad

I will look at it again and possibly consult with the Capacitor team to see what solution they recommend.

robingenz avatar Aug 02 '22 18:08 robingenz

@leochaddad - I figured out a workaround to the problem for my vuejs application, I set skipNativeAuth to false

  • See Video - https://www.loom.com/share/b6b33bd9838a4af3a26d6b29595c8e0b

and then I just use the JS SDK authListener since it does fire.

    const initialized = ref(false);

    const app = initializeApp(firebaseConfig);
    if (Capacitor.isNativePlatform()) {
        auth = initializeAuth(app, {
            persistence: indexedDBLocalPersistence,
        });
    } else {
        auth = getAuth(app);
    }

    auth.onAuthStateChanged(async (user: any) => {
        console.log("user - onAuthStateChanged", user);
        initialized.value = true;
    })

I don't start the app completely until I know authStateChanged has been called

const { initialized } = useFirebaseService();

const app = createApp(App)
  .use(IonicVue)
  .use(router);

watch(initialized, (value) => {
  console.log("initialized", value);
  // mount the app
  console.log('mounting the app...');
  router.isReady().then(() => {
    app.mount('#app');
  });
})

aaronksaunders avatar Aug 02 '22 23:08 aaronksaunders

Sorry I wasn't able to give a reproducible, I have used a similar method to @aaronksaunders - I am authed wooohooo... Then comes the fun part, on web I can use Firestore DB perfectly as the token is sent with the request and handled as per the Firebase docs.

However, trying to fire a web based firestore request from iOS causes a not authed error - I cannot figure out why this would be the case, I know the firestore connection works because I am able to read data etc.

Any ideas on that, seems like the current authed token is not actually passed back and used within iOS unless I am missing something

jaythegeek avatar Aug 05 '22 17:08 jaythegeek

@jaythegeek - not sure what you are doing but if you followed my approach it should work fine. I just retested my code and I am able to query the database on web and on device.

check out project here - https://github.com/aaronksaunders/mobile-gauth

aaronksaunders avatar Aug 05 '22 18:08 aaronksaunders

@aaronksaunders do you have a security rule on that query such as;

match /users/{userId} {
      allow write: if request.auth.uid == userId;
      allow read: if true;
}

The request.auth is not set when performing this from iOS but does work from web, it's strange.

I am imagining your query doesnt have a rule on it like pretty much all of my reads, so it works fine, but just not the authed calls for writes etc.

Any further suggestions would be appreciated, I have no hair as it is! 😂

Looking over your code now and we are pretty much on the same page :/

jaythegeek avatar Aug 05 '22 18:08 jaythegeek

@jaythegeek I have a security rule - also the way my code is written, I can't even get to the query until firebase js SDK fires the authChangedListener, that is why I don't think you are doing exactly what I am doing

aaronksaunders avatar Aug 05 '22 18:08 aaronksaunders

@aaronksaunders I have literally taken your firebase.ts and swapped out the config, then the write I am doing to firestore is like this:

import { useFirebaseService } from "@/services/firebase";
const { db } = useFirebaseService();
// other imports

function updateUser(){
  await setDoc(
        doc(db, "users", user.id),
        {
          thing: 'other thing',
        },
        { merge: true }
      );
 }

All my reads and anything without a security rule works great, if I turn that particular security rule off, it works perfectly.

jaythegeek avatar Aug 05 '22 19:08 jaythegeek

@jaythegeek can you show me what the output is in your console log?

also, show me how you are ensuring the js initialization has been completed before you render the first component of your app.

happy to move this chat to a discussion since we are off-topic here, this is all a workaround because the plugin doesn't work

aaronksaunders avatar Aug 05 '22 19:08 aaronksaunders

@aaronksaunders I have opened a discussion - Yes frustrating have to work around, this is the core function of the plugin :/ @robingenz if we figure it out, I'll come back and update here as it may help to resolve the underlying issue we are working around.

jaythegeek avatar Aug 05 '22 19:08 jaythegeek

Yes, feel free to let me know, I appreciate any help. PRs are also welcome! I'm a bit busy at the moment as I only maintain these open source projects in my spare time and I'm currently updating more than 15 plugins to Capacitor 4. I will try to work on this issue in a timely manner. If you can't find a solution and want me to invest extra time and prioritize your issue you are welcome to sponsor this issue once, see https://github.com/sponsors/capawesome-team?frequency=one-time.

robingenz avatar Aug 05 '22 19:08 robingenz

@robingenz I appreciate your position, if we can fix it we will. I am snowed under too and this particular problem is causing me a delay, so I am sure, in the event a few more hours doesn't solve it, we can come to an arrangement 😄

jaythegeek avatar Aug 05 '22 19:08 jaythegeek

@robingenz - I appreciate the work, you asked for a sample showing the bug, and I provided it to you... not sure what else there is you are expecting other than I fix the bug and submit a PR.

The fact is the plugin doesn't work, IMHO this is not a bug, it is a missing feature and everything I am doing is a hack to make it work

aaronksaunders avatar Aug 05 '22 19:08 aaronksaunders

@aaronksaunders That's right I have all the info I need to solve the problem. Thanks for that! I just wanted to let you know why it's waiting so long and what the options are. The issue is just waiting for me to find time for it. That's all! As I said, I do this in my spare time, which is limited. You can send a PR if you want to speed up the process. Or you can sponsor the project. If neither is an option, I can only do it when I find time.

robingenz avatar Aug 05 '22 20:08 robingenz