59

I've start working with new Firebase SDK.

When I'm doing user login, I'm onAuthStateChanged method is being called twice with same state (etc. user sign in).

I'm sure I'm adding the AuthStateListener only once to the FirebaseAuth reference.

Any help?

8
  • where is your code? Commented Jun 7, 2016 at 8:09
  • This problem exists in my code too. The onStart() has only this code besides calling super: FirebaseAuth.getInstance().addAuthStateListener(this);. This call is not found anywhere else and I checked that the onStart() is being called only once. Yet, the onAuthStateChanged(...) method is called twice (or three times sometimes). Commented Jun 7, 2016 at 16:09
  • 3
    Probably a bug, use a flag in the meantime like in this answer: stackoverflow.com/a/37686371/3597165 Commented Jun 7, 2016 at 18:14
  • 1
    Possible duplicate of Firebase Android onAuthStateChanged() fire twice after signInWithEmailAndPassword() Commented Jun 18, 2016 at 20:02
  • 1
    Not fixed in 9.4.0 Commented Aug 2, 2016 at 12:35

6 Answers 6

63

Yes, and this is very annoying. This is due a registration call. Not only that, onAuthStateChanged is going to be called many times in many different states, with no possibility of knowing which state it is.

Documentation says:

onAuthStateChanged(FirebaseAuth auth)

This method gets invoked in the UI thread on changes in the authentication state:

  • Right after the listener has been registered

  • When a user is signed in

  • When the current user is signed out

  • When the current user changes

  • When there is a change in the current user's token

Here some tips to discover the current state:

  • Registration call: skip the first call with a flag.
  • User signed in: user from parameter is != null.
  • User signed out: user from parameter is == null.
  • Current user changes: user from parameter is != null and last user id is != user id from parameter
  • User token refresh: user from parameter is != null and last user id is == user id from parameter

This listener is a mess and very bugprone. Firebase team should look into it.

Sign up to request clarification or add additional context in comments.

5 Comments

I concur. This issue exists on iOS currently. Thanks for sorting out the states for us.
Even after nearly 5 years the problem still exists...
Even after nearly 6 years.... Don't be surprised if this never gets addressed (gist.github.com/chitchcock/1281611)
I have the exact same problem, i think the only way forward to debouncing it... Its annoying as its very handy to have onOuthStateChanged looking after the session, but make it more hassle than what it worth due to this issue.
Its August 2024, this problem still exists
12

While the other answers provided here might do the job, I find managing a flag cumbersome and error-prone.

I prefer debouncing the event within short periods of time. It is very unlikely, maybe even impossible, for a user to login then logout within a period of 200ms let's say.

TLDR

Debouncing means that before handling an event, you wait to see if the same event is gonna fire again within a predefined period of time. If it did, you reset the timer and wait again. If it didn't, you handle the event.

This is an Android question, which is not my field, but I'm sure android provides some kind of tool that can help with the task. If not, you can make one using a simple timer.

Here's how a Javascript implementation might look like:

var debounceTimeout;
const DebounceDueTime = 200; // 200ms

function onAuthStateChanged(auth)
{
    if (debounceTimeout)
        clearTimeout(debounceTimeout);

    debounceTimeout = setTimeout(() =>
    {
        debounceTimeout = null;

        handleAuthStateChanged(auth);
    }, DebounceDueTime);
}

function handleAuthStateChanged(auth)
{
    // ... process event
}

1 Comment

Best solution IMHO. Wish I saw this answer before writing the if else ladder. Yikes.
8

My workaround is to use a Boolean declared globally to flag if onAuthStateChanged has need called before.

private Boolean authFlag = false;
 mAuthListener = new FirebaseAuth.AuthStateListener() {
        @Override
        public void onAuthStateChanged(@NonNull final FirebaseAuth firebaseAuth) {
            if (firebaseAuth.getCurrentUser() != null) {
                if(authFlag== false) {
                    // Task to perform once
                    authFlag=true;
                }
            }
        }
    };

2 Comments

Thanks simple but gets the job done when you dont want to waste more time.
Could someone offer me some more clarification over this please?
0

Usually I want to setup the UI before adding the listener and repeat the setup any time the auth state changes (avoiding the initial double call). My solution is to enhance the boolean flag solution and keep track of the uid (not the token) of the last user, which may be null.

private FirebaseAuth firebaseAuth;
private String lastUid; // keeps track of login status and changes thereof

In onCreate, I get the auth instance and set the UI accordingly, before adding the listener in onStart

@Override
protected void onCreate(Bundle savedInstanceState){
    ...
    firebaseAuth = FirebaseAuth.getInstance();
    getUserSetUI();
    ...
}

where getUserSetUI sets lastUid according to the auth instance

private void getUserSetUI(){
    lastUid = (firebaseAuth == null || firebaseAuth.getCurrentUser() == null) ?
            null : firebaseAuth.getUid();
    setUI(!(lastUid == null));
}

The listener checks to see if the state has actually changed

@Override
public void onAuthStateChanged(@NonNull FirebaseAuth auth){
    String uid = auth.getUid(); // could be null
    if( (uid == null && lastUid != null) || // loggedout
            (uid != null && lastUid == null) || // loggedin
            (uid != null && lastUid != null && // switched accounts (unlikely)
                    !uid.equals(lastUid))){
        getUserSetUI();
    }
}

Comments

0
if (authStateListener == null) {
        authStateListener = new FirebaseAuth.AuthStateListener() {
            @Override
            public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) {
                if (firebaseAuth.getCurrentUser() == null) {
                    //Do anything here which needs to be done after signout is complete
                    FirebaseAuth.getInstance().removeAuthStateListener(this);
                    Log.d(TAG_, "logout");


                    finish();
                } else {
                }
            }
        };
    }

FirebaseAuth.getInstance().removeAuthStateListener(this) should be called

Comments

0
  try{
       FirebaseAuth.getInstance().signOut();
       FirebaseInstanceId.getInstance().deleteInstanceId();
   }catch (Exception ex){
       ex.printStackTrace();
   }

When user logout from gmail then the user should also be logout from firebase. This is how I resolved this issue.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.