Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

firebase auth delayed on refresh

Hard refreshes on my SPA React/Firebase application does not maintain auth state on immediate execution of a function. I have a workaround, but it's sketchy.

My react routes utilize the onEnter function to determine whether or not the user is authenticated or not. For instance

<Route path="/secure" component={Dashboard} onEnter={requireAuth}/>

Furthermore, my requireAuth function looks like this:

function (nextState, replace) {
        console.log('requireAuth', firebase.auth().currentUser);
        if (!firebase.auth().currentUser) {
            console.log('attempting to access a secure route. please login first.');
            replace({
                pathname: '/login',
                state: { nextPathname: nextState.location.pathname }
            });
        }
};

However, on a hard refresh there is a slight delay on firebase.auth().currentUser. It's null at first, then executes a POST to firebase servers in order to determine auth state. When it returns the currentUser object is populated. This delay causes issues though.

My hacky solution is the following: update: this doesn't actually work...

function (nextState, replace) {
    setTimeout(function () {
        console.log('requireAuth', firebase.auth().currentUser);
        if (!firebase.auth().currentUser) {
            console.log('attempting to access a secure route. please login first.');
            replace({
                pathname: '/login',
                state: { nextPathname: nextState.location.pathname }
            });
        }
    }, 50);
};

Simply wrap it in a timeout. However, I really don't like this... any thoughts?

Update:

I have also tried to wrap it within a onAuthStateChanged listener, which should be more accurate than a setTimeout with a definitive time delay. Code as follows:

function (nextState, replace) {
    var unsubscribe = firebase.auth().onAuthStateChanged(function (user) {
        if (!user) {
            console.log('attempting to access a secure route');
            replace({
                pathname: '/login',
                state: { nextPathname: nextState.location.pathname }
            })
            console.log('should have called replace');
        }
        unsubscribe();
    });
    // setTimeout(function () {
    //     console.log('requireAuth', firebase.auth().currentUser);
    //     if (!firebase.auth().currentUser) {
    //         console.log('attempting to access a secure route. please login first.');
    //         replace({
    //             pathname: '/login',
    //             state: { nextPathname: nextState.location.pathname }
    //         });
    //     }
    // }, 50);
};

The two log statements are executed, but react-router replace does not seem to be executed correctly. Perhaps that's a different question for the react-router experts.

update 2:

It was late at night when I was working on this. Apparently setTimeout doesn't actually work either.

like image 648
James Gilchrist Avatar asked May 22 '16 04:05

James Gilchrist


Video Answer


1 Answers

Works by managing localStorage. Here is example how I do it.

  constructor(props) {
    super(props);

    let authUser = null;

    // setting auth from localstorage
    for (let key in localStorage) {
      if (key === storageId) {
        authUser = {};
        break;
      }
    }

    this.state = {authUser};
  }

  componentDidMount() {
    firebase
      .auth
      .onAuthStateChanged(authUser => {

        if (authUser) {
          localStorage.setItem(storageId, authUser.uid);
        } else {
          localStorage.removeItem(storageId);
        }

        // change state depending on listener
        authUser
          ? this.setState({authUser})
          : this.setState({authUser: null});
      });
  }
like image 56
Himanshu Mishra Avatar answered Sep 28 '22 05:09

Himanshu Mishra