firebase-android-sdk
firebase-android-sdk copied to clipboard
Users get logged out and remain in invalid authentication state
[READ] Step 1: Are you in the right place?
We use FirebaseAuthentication, which is part of the sdk, so I think yes.
[REQUIRED] Step 2: Describe your environment
- Android Studio version: Android Studio Chipmunk | 2021.2.1 Patch 1 Build #AI-212.5712.43.2112.8609683
- Firebase Component: Authentication
- Component version: 21.0.1 with bom 29.1.0
Updated dependencies in the affected releases:
com.google.android.gms:play-services-auth:20.1.0 -> com.google.android.gms:play-services-auth:20.2.0 com.android.tools.build:gradle:7.0.4 -> com.android.tools.build:gradle:7.1.3 com.google.firebase:firebase-bom:29.1.0 -> com.google.firebase:firebase-bom:29.3.1
[REQUIRED] Step 3: Describe the problem
TL;DR:
Users loose their registration and end up in an invalid anonymous registered state.
FirebaseAuth.getCurrentUser
probably returns null
, even though there is a user and
auth is initialized. FirebaseUser.isAnonymous
returns false
, even though the account is
only registered using anon registration.
Long:
It's really not easy to describe the problem, so I'll try to explain the timeline and setup.
We use Firebase for authentication and it's realtime database to build a community feature within our application, where users have profiles. On first app start, users get signed in anonymously, until they decide to use a real auth provider, after which the anon and "real" account get linked. So far, so normal.
After a new release we noticed within our internal test flavor, that some of the registred (non anon) users
loose their names and profiles and can no longer interact with the community.
We also noticed thousands of database permission excpetions and almost the same amount
of FirebaseNetworkException
s.
I investigated a company owned device that was affected and it actually turns out that the affected "user" belongs to an anonymous regsitration (UID -> console), although the owner of the phone was registered in our community through google since 1.5 years before that.
The only option to go back from a google/facebook etc registration to an anonymous registration,
is through logging out!
And if you log out, we sign you back in anonymously, but we will not make DB calls anymore,
since FirebaseUser.isAnonymous
returns true
in this case.
But in our case here, the client "thinks" that it still has a profile and is normally registered (google/facebook etc.).
Therefore the client always tries to read and write DB notes, which are restricted for anon users
and which we code-wise prohibit by calling the FirebaseAuthentication
typicall methods.
Till today, we have almost like 10k affected users.
Steps to reproduce:
The only way I see that a normal registered user is "again anonymous", (and which I was
kind of able to reproduce), would be if FirebaseAuth.getCurrentUser
returns null
,
even though there is a normal registered account.
In this case we would wrongly call signInAnonymously
OVER the current registration.
I tried it, and if you do this, the FirebaseUser
will end up in an invalid state and return FALSE
for
FirebaseUser.isAnonymous
, even though getProviderData
will only return 1 entry containing "firebase"
.
This would in our case result in executing multiple DB-write approaches, which will of course fail
and might be the reason for the thousands Database Permission Erros.
I don't know how FirebaseAuth.getCurrentUser
would return null
for normal account though.
Maybe the thousands occurences of FirebaseNetworkException
are related to FirebaseAuth
returning null
for current user.
I will provide a minimal repo, to reproduce the isAnonymous == false
issue.
And I will provide a small code snippet of our authentication app startup.
Our authentication app startup procedure is very simple.
- Initialize Firebase auth state
- Check for a user 2a. If there is non, register an anonymous account 2b. If there is one, make a quick DB call, but only if the account is not anonymous
Relevant Code:
// Called at app startup once
private void initializeFirebaseAuth() {
FirebaseAuth.getInstance().addAuthStateListener(new FirebaseAuth.AuthStateListener() {
@Override
public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) {
firebaseAuth.removeAuthStateListener(this);
FirebaseUser user = firebaseAuth.getCurrentUser();
if (user != null) {
writeToDb(user);
} else {
signInAnonymously(user);
}
}
});
}
private void writeToDb(FirebaseUser user){
if(user.isAnonymous){
Timber.w("Some warning");
}else{
//FirebaseDatabase.someCall ...
}
}
private void signInAnonymously(FirebaseUser user) {
if(user != null){
Timber.w("Some warning");
return;
}
firebaseAuth.signInAnonymously().addOnCompleteListener(task -> {
if (task.isSuccessful()) {
Timber.d("Nice");
}
});
}
}
Exceptions:
FirebaseNetworkException
- first occurence May 5h, 2644 non-fatal events affecting 1074 users in the last 90 days
Non-fatal Exception: com.google.firebase.FirebaseNetworkException: A network error (such as timeout, interrupted connection or unreachable host) has occurred.
at com.google.android.gms.internal.firebase-auth-api.zzti.zza(com.google.firebase:firebase-auth@@21.0.3:17)
at com.google.android.gms.internal.firebase-auth-api.zzuc.zza(com.google.firebase:firebase-auth@@21.0.3:1)
at com.google.android.gms.internal.firebase-auth-api.zzud.run(com.google.firebase:firebase-auth@@21.0.3:3)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:255)
at android.app.ActivityThread.main(ActivityThread.java:8194)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:632)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1049)
Which is probably causing:
DatabaseException
- first occurence May 8h, 79531 non-fatal events affecting 21038 users in the last 90 days
Non-fatal Exception: com.google.firebase.database.DatabaseException: Firebase Database error: Permission denied
at com.google.firebase.database.DatabaseError.toException(DatabaseError.java:230)
at com.google.firebase.database.core.utilities.Utilities$1.onComplete(Utilities.java:253)
at com.google.firebase.database.core.Repo$7.run(Repo.java:435)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:233)
at android.os.Looper.loop(Looper.java:344)
at android.app.ActivityThread.main(ActivityThread.java:8204)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:589)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1071)
Minimal repo - showcase isAnonymous
wrong state
https://github.com/SteveBurkert/firbease-zombie-user-example
(The repo only showcases how you can reproduce to make FirebaseUser.isAnonymous
return false
, even though it is anonymously registered.)
Thanks in advance.
I couldn't figure out how to label this issue, so I've labeled it for a human to triage. Hang tight.
Thanks for the detailed explanation and reproducible example, @SteveBurkert. I'll notify our engineers and see what we can do here.
Hi, thanks for filing this issue! We are unable to promise any timeline for this, but if others also have this issue, adding a +1 on this issue can help us prioritize adding this to the roadmap.
(Googler-only internal tracking bug: b/238776633)
We recently noticed a similar behaviour. Maybe it's related so I'm going to explain our findings:
First of all, we're using Anonymous Authentication in our app. We call signInAnonymously()
during app start and use the (anonymous) user ID as a reference to user data in various Firestore collections and documents.
A few weeks ago users started complaining that they are losing their data on every app start. We looked into this and could reproduce a case where signInAnonymously()
would return a new user on every call. Of course no app data was deleted between the executions. The app was just terminated and started again. Obviously with a new user ID, references to previously stored data in Firestore are lost which goes along with the user reportings. However when we manually cleared the app data and started the app, this faulty behaviour was gone and we received the same user ID on every app start. This is also confirmed by our affected users.
We assume that some update of the firebase-auth
dependency introduced this erroneous state which can only be fixed by clearing application data.
Additionally we noticed a few FirebaseNetworkExceptions
in Crashlytics coming from firebase-auth
which could be the reason for this problem? In accordance with OP's findings it seems that most of these exceptions started to occur with version 21.0.3
of firebase-auth
(no guarantees though as we can only look back up to 90 days in Crashlytics). These exceptions still occur with the current version 21.0.6
. Some exemplary exception below:
Caused by com.google.firebase.FirebaseNetworkException: A network error (such as timeout, interrupted connection or unreachable host) has occurred.
at com.google.android.gms.internal.firebase-auth-api.zztu.zza(com.google.firebase:firebase-auth@@21.0.6:17)
at com.google.android.gms.internal.firebase-auth-api.zzus.zza(zzus.java:9)
at com.google.android.gms.internal.firebase-auth-api.zzut.zzl(com.google.firebase:firebase-auth@@21.0.6:1)
at com.google.android.gms.internal.firebase-auth-api.zzuq.zzh(com.google.firebase:firebase-auth@@21.0.6:25)
at com.google.android.gms.internal.firebase-auth-api.zzts.zzh(zzts.java:1)
at com.google.android.gms.internal.firebase-auth-api.zzqq.zza(zzqq.java:2)
at com.google.android.gms.internal.firebase-auth-api.zzvb.zza(zzvb.java:16)
at com.google.android.gms.internal.firebase-auth-api.zzuh.zzf(com.google.firebase:firebase-auth@@21.0.6:4)
at com.google.android.gms.internal.firebase-auth-api.zzrx.zzp(zzrx.java:4)
at com.google.android.gms.internal.firebase-auth-api.zztt.zzj(zztt.java:5)
at com.google.android.gms.internal.firebase-auth-api.zzsh.zzc(com.google.firebase:firebase-auth@@21.0.6:1)
at com.google.android.gms.internal.firebase-auth-api.zzsh.zzc$bridge(com.google.firebase:firebase-auth@@21.0.6:35)
at com.google.android.gms.internal.firebase-auth-api.zzuu.run(com.google.firebase:firebase-auth@@21.0.6:1)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:920)
We hope this information helps in identifying and fixing the problem soon 🤞🏼
Hey there. Any updates on this issue?
We recently added some massive logging on our end and we've found that on some permission denials, related to the database rules, we got users with weird provider data, that seem to have the above described problem.
Here is one of the logs from crashlytics with a dedicated custom exception:
This list shows all the providers the user is signed up with...
Firebase SDK has illegal auth state. Is not anonymous user by SDK, but only has anon provider id. User: 'User.providers = [firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, firebase, fireb<truncated: 1267558 chars>
May I highlight <truncated: 1267558 chars> here! Each provider takes up 10 characters in space (including the comma and one whitespace). This means, that this user is roughly signed up 130 thousand times(!) with anon provider. What the...
SDK internally isAnonymous
is checked also by size of the provider info array. If it's > 1 it will directly return false.
Which is the case here. But how can this type of provider mess happen?
The provider id firebase
comes from https://firebase.google.com/docs/reference/android/com/google/firebase/auth/FirebaseAuthProvider#PROVIDER_ID
right?
And FirebaseAuthProvider
is only anonymous sign ups, right?
So how can we end up with 20+ anonymous providers for one user?
We have like thousand of users, having this issue.
@lisajian
Hello, I would also like to ask what the progress is on this issue? We're still seeing a lot of FirebaseNetworkException
in firebase-auth
, actually it's the number one crash of our app constantly occurring, but I doubt that all affected users have a bad network connection when this happens.
Caused by com.google.firebase.FirebaseNetworkException: A network error (such as timeout, interrupted connection or unreachable host) has occurred.
at com.google.android.gms.internal.firebase-auth-api.zzxc.zza(com.google.firebase:firebase-auth@@21.1.0:19)
at com.google.android.gms.internal.firebase-auth-api.zzya.zza(zzya.java:9)
at com.google.android.gms.internal.firebase-auth-api.zzyb.zzl(com.google.firebase:firebase-auth@@21.1.0:1)
at com.google.android.gms.internal.firebase-auth-api.zzxy.zzh(com.google.firebase:firebase-auth@@21.1.0:25)
at com.google.android.gms.internal.firebase-auth-api.zzxa.zzh(zzxa.java:1)
at com.google.android.gms.internal.firebase-auth-api.zzty.zza(zzty.java:2)
at com.google.android.gms.internal.firebase-auth-api.zzyj.zza(zzyj.java:16)
at com.google.android.gms.internal.firebase-auth-api.zzxp.zzf(com.google.firebase:firebase-auth@@21.1.0:4)
at com.google.android.gms.internal.firebase-auth-api.zzvf.zzp(zzvf.java:4)
at com.google.android.gms.internal.firebase-auth-api.zzxb.zzj(zzxb.java:5)
at com.google.android.gms.internal.firebase-auth-api.zzvp.zzc(com.google.firebase:firebase-auth@@21.1.0:1)
at com.google.android.gms.internal.firebase-auth-api.zzyc.run(com.google.firebase:firebase-auth@@21.1.0:1)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1137)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:637)
at java.lang.Thread.run(Thread.java:1012)
Hi all, this issue is now fixed in Firebase BoM version 32.0.0 or Authentication version 22.0.0. That said, I'll close this issue now. Feel free to reply back here if there's any issue. Thanks!
@argzdev Wow, that are great news, thank you! 👍🏼 What was the reason for the wrong authentication state and what was the fix? I'm curious and would like to know 😃
Hi @svenjacobs, according to the internal bug link, it seems that this is the possible reason:
This issue is because if we already have a user instance and get a new one to update, we only update the provider list, we do not change the uid.
That are really great news! May I ask, does this solve both of the problems, meantioned in the discussion? And are affected user accounts "self healing"?
- "
FirebaseUser.isAnonymous
returnsfalse
, even though the account is only registered using anon registration." - Duplicated entries in provider list: "...user is roughly signed up 130 thousand times(!) with anon provider." (https://github.com/firebase/firebase-android-sdk/issues/3885#issuecomment-1446540815)
Thank you, @argzdev
Hi @SteveBurkert, the fix is only for the first issue.
"FirebaseUser.isAnonymous returns false, even though the account is only registered using anon registration."
As for the second issue with 130 providers duplicated, this is not yet fixed. We aren't exactly sure what causes the duplicate providers, we're still trying to figure this out, so we can't guarantee that the fix for the first issue also affects the second issue.