Firebase Auth

If I had to hazard a guess about the most frequent point of confusion when starting with Firebase Authentication, it would be the currentUser API. While it seems simple enough to get an object representing the currently signed in user, there is one hidden complexity.

Auth state seems binary: null or non-null (or is it?)

Here's an easy enough bit of code in JavaScript that checks to see if a user is signed in:

const user = firebase.auth().currentUser
if (user) {
    // user is signed in, show user data
}
else {
    // user is signed out, show sign-in form
}

Here's the equivalent in Java:

FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
if (user != null) {
    // user is signed in, show user data
}
else {
    // user is signed out, show sign-in form
}

And Swift:

let user = Auth.auth().currentUser
if user != nil {
    // user is signed in, show user data
}
else {
    // user is signed out, show sign-in form
}

In all three of these cases, an object property or accessor method is used to check the state of the currently signed in user. While this works sometimes, you might notice that it doesn't work as expected when the app is launched cold, or the web page is refreshed. The current user object ends up being null, even if the user previously signed in.

This is extra confusing given that the documentation suggests that the signed in user is automatically persisted by default between app launches and page reloads. If information about the signed-in user persists as documented, why is the object still null? And even more confusing, why only on a cold start like this?

Let's dig in!

The current user is object is obtained asynchronously

One thing to realize about the Firebase SDKs is that its APIs are all asynchronous. This means that the SDK is not going to block the main thread of execution in order to deliver data — the object containing the currently signed in user is no exception. In fact, the Firebase Auth SDK requires at least one, and…