10
votes

Precondition

$npm install --save firebase@4.11.0

issue

I'm using firebase authentication on my web application. In my app, I implemented onAuthStateChanged for client side js like below.

firebase.auth().onAuthStateChanged((user) => {
  if(user) {
    //logged in
  } else {
    //do sth
  }
});

After login, I confirmed this method will return actual user obj, but if I refresh the page, then user might be null. Curiously, sometimes user won't be null. I'm afraid there are some limitation of calling onAuthStateChanged, but currently I have no idea.

How should I deal with this issue?

update

Let me share my minimal example.

My app is working with express.js. There are two URLs like below. /login /main

In the login page, I implemented authentication method. If the login is successfully finished, then user will be redirected to '/main'.

//login.js
import firebase from 'firebase';
var config = {...};
firebase.initializeApp(config);

var provider = new firebase.auth.GoogleAuthProvider();
firebase.auth().signInWithPopup(provider)
.then((result) => {
  return result.user.getIdToken(true);
}).then((idToken) => {
  if(idToken) {
    location.href = '/main';
  }
});

In the main page, there is no login method. main.js is only checking whether user is logged in.

//main.js
import firebase from 'firebase';
var config = {...};
firebase.initializeApp(config);

firebase.auth().onAuthStateChanged((user) => {
  if (user) {
    //initialize main page.
  } else {
    location.href = '/login';
  }
}

I think login status is stored on LocalStorage of web browser. This means that, after finishing loading of main.js, onAuthStateChanged will be automatically fired with user information, but not working as I expected. I'm sure that persistence of login information is correct because official document says the default setting is LOCAL for web client.

https://firebase.google.com/docs/auth/web/auth-state-persistence

my question

Should I implement onAuthStateChanged with another way? How can I ensure user is logged in after reload?

e.g.

import $ from 'jquery';
$(document).on('ready', () => {
  onAuthStateChanged((user) => {...});
});

Or could you show me the correct way?

Workaround

I decided to remove session and set redirection to login page if null is returned. This is not a solution, but a workaround currently...

3
.onAuthStateChanged is asynchronous and resolves null for a microsocond on page load/refresh. That's how it works. You can grey out or put loader gif's . - Ronnie Royston
Do you mean that, after reload, the first onAuthStateChanged event will be fired with null user and after a few secounds the event will be re-fired with actual user info? I implemented loading gif for 10 seconds until loading is finished like you mentioned, however, unfortunately my onAuthStateChanged is fired only once with null user... - Osamu Tokita
try the web app / your app on another browser. I've noticed that if you set Firefox, for example, to never remember History (presumably turning off storage), my auth fails w no meaningful error. - Ronnie Royston
Are you using hosting rewrites? The delay is quite significant for me, but it goes away completely if I do not use hosting rewrites. - John Angland

3 Answers

5
votes

You're not calling onAuthStateChanged. Instead you're telling Firebase to call you when the authentication state changes, which may happen a few times when the page is being re-loaded

When a page is getting loaded and there was previously a user signed in, the auth state may change a few times, while the client is figuring out if the user's authentication state it still valid. For that reason, you may see a call with no user before seeing the final call with the actual signed in user.

1
votes

The fact it's sometimes null and sometimes not null likely points to an async problem. Are you making the check in the if statement above? All references to the user should be within the callback. If that all checks out, maybe check that authentication is being properly initiated.

1
votes

onAuthStateChanged is an observer as stated in firebase docs, which gets triggered when the auth state is changed like user signed in, signed out, pwd change. To check if user is logged in or not you should use firebase.auth().currentUser which will give you the current logged in user. As you said your state is local firebase.auth().currentUser will always give you user unless user is signed out.