Firebase Auth and Vue-router

To build on Richard's answer, for those who are using regular Vue (not Vuex)

main.js

//initialize firebase
firebase.initializeApp(config);
let app: any;
firebase.auth().onAuthStateChanged(async user => {
if (!app) {
    //wait to get user
    var user = await firebase.auth().currentUser;

    //start app
    app = new Vue({
      router,
      created() {
        //redirect if user not logged in
        if (!user) {
          this.$router.push("/login");
        }
      },
      render: h => h(App)
    }).$mount("#app");
  }
});

router.js

//route definitions
//...
router.beforeEach((to, from, next) => {
  const currentUser = firebase.auth().currentUser;
  const requiresAuth = to.matched.some(record => record.meta.requiresAuth);

  if (requiresAuth && !currentUser) {
    const loginpath = window.location.pathname;   
    next({ name: 'login', query: { from: loginpath } });
  } else if (!requiresAuth && currentUser) {
    next("defaultView");
  } else {
    next();
  }
});

Firebase always triggers an auth state change event on startup but it's not immediate.

You'll need to make authService.authenticated return a promise in order to wait for Firebase to complete its user/auth initialisation.

const initializeAuth = new Promise(resolve => {
  // this adds a hook for the initial auth-change event
  firebase.auth().onAuthStateChanged(user => {
    authService.setUser(user)
    resolve(user)
  })
})

const authService = {

  user: null,

  authenticated () {
    return initializeAuth.then(user => {
      return user && !user.isAnonymous
    })
  },

  setUser (user) {
    this.user = user
  },

  login (email, password) {
    return firebase.auth().signInWithEmailAndPassword(email, password)
  },

  logout () {
    firebase.auth().signOut().then(() => {
      console.log('logout done')
    })
  }
}

You don't need to call setUser from the signInWith... promise as this will already be handled by the initializeAuth promise.