Vuefire: Using vuefire with Auth (getting currentUser)

Created on 22 Jan 2017  路  8Comments  路  Source: vuejs/vuefire

I've been looking at #18, #47 and #48, but I'm still confused about the best way to deal with authentication using vuefire.

On my main.js I'm using woodberry's approach and calling the unsubscribe function after authentication:

const unsubscribe = fb.auth().onAuthStateChanged((user) => {
  new Vue({
    el: '#app',
    template: '<App/>',
    components: { 
      App 
    },
    router
  });

  // stop listening
  unsubscribe()
})

My understanding is that by using this pattern, we delay the creating of the Vue instance until Firebase authenticates the user.

On my routes.js I use beforeEnter navigation guards to ensure that the user is authenticated:

    function requireAuth(to, from, next) {
      if (!fb.auth().currentUser) {
        console.log("User is not logged in");
        next({
          path: '/auth',
          query: { redirect: to.fullPath }
        })
      } else {
        console.log("User is logged in:", auth.currentUser.uid);
        next()
      }
    }

    export default [
      { 
        path: '/students', 
        component: StudentIndex,
        beforeEnter: requireAuth
      }
    ]

Then on my StudentIndex.vue component, I access the firebase database reference /users/${uid}/students, but when I load the http://mydomain.com/students URL, I get an error:

  export default {
    // this doesn't work: Uncaught TypeError: Cannot read property 'uid' of null
    firebase: {
      students: firebase.database().ref('users')
                  .child(firebase.auth().currentUser.uid)
                  .child('students')
    }
  }

However, if I create the reference manually using $bindAsArray in the created hook, it works:

  export default {
    // this works
    created() {
      this.$bindAsArray('students', 
        firebase.database().ref('users')
          .child(firebase.auth().currentUser.uid)
          .child('students')
        )
    }
  }

Is this to be expected or am I missing something?

The thought of having to define my firebase references differently depending on whether the path includes the user id or not seems a little weird, so I'm sure I must be missing something.

question

Most helpful comment

I am using following approach with vue-router & vuex:

App.vue (wrapper component of whole application):

<template>
  <!-- router-view wont be rendered unless user is either authenticated or not -->
  <!-- you can display loader here for example -->
  <router-view v-if="user !== null" />
</template>

<script>
  import Firebase from 'firebase';
  import { mapState } from 'vuex';

  export default {
    computed: {
      ...mapState(['user']),
    },
    beforeCreate () {
      Firebase.auth().onAuthStateChanged((user) => {
        // initially user = null, after auth it will be either <fb_user> or false
        this.$store.commit('setUser', user || false);
        if (user && this.$route.path === '/login') {
          this.$router.replace('/');
        } else if (!user && this.$route.path !== '/login') {
          this.$router.replace('/login');
        }
      });
    },
  };
</script>

store.js:

import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    user: null,
  },
  mutations: {
    setUser (state, value) {
      state.user = value;
    },
  },
});

SomeComponent.vue

<template><div>{{ user.email }}</div></template>
<script>
import { mapState } from 'vuex';
export default {
  computed: {
      ...mapState(['user']),
    },
}
</script>

All 8 comments

I guess an alternative could be this:

    firebase(){
      const userId = firebase.auth().currentUser.uid
      return {
        students: db.ref('users')
                    .child(userId)
                    .child('students')
      }
    }

About the Uncaught error, that's normal, the code gets executed before the component is actually used. Using the function syntax, as you pointed out is the way to go.

About the user authentication, I'd personally not wait until the user is authenticated but rather use a different view to auth. That'd create a better UX and faster load time, but your approach is perfectly fine too.

@posva Thanks, do you have an example of using a dedicated view to authenticate? The reason why I used this approach is because I don't want users to log in every time they refresh the page

Sorry, I don't 馃槙
Basically the view at / would trigger that in some hook and then redirect to an authed view
Or a redirect would always go to a /login view if not authenticated

I am using following approach with vue-router & vuex:

App.vue (wrapper component of whole application):

<template>
  <!-- router-view wont be rendered unless user is either authenticated or not -->
  <!-- you can display loader here for example -->
  <router-view v-if="user !== null" />
</template>

<script>
  import Firebase from 'firebase';
  import { mapState } from 'vuex';

  export default {
    computed: {
      ...mapState(['user']),
    },
    beforeCreate () {
      Firebase.auth().onAuthStateChanged((user) => {
        // initially user = null, after auth it will be either <fb_user> or false
        this.$store.commit('setUser', user || false);
        if (user && this.$route.path === '/login') {
          this.$router.replace('/');
        } else if (!user && this.$route.path !== '/login') {
          this.$router.replace('/login');
        }
      });
    },
  };
</script>

store.js:

import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    user: null,
  },
  mutations: {
    setUser (state, value) {
      state.user = value;
    },
  },
});

SomeComponent.vue

<template><div>{{ user.email }}</div></template>
<script>
import { mapState } from 'vuex';
export default {
  computed: {
      ...mapState(['user']),
    },
}
</script>

@alekbarszczewski why are you using mutations instead of actions in this example? specifically: this.$store.commit('setUser', user || false);
Isn't it best practice to this.$store.dispatch('setUser', user || false)? Though I can't get this to work. Is it something to do with async?

@mdmullins It's probably specific to your app so you should ask on the forum.
setUser is probably just doing state.user = user so it's better to have a mutation there

@mdmullins Action is not required here because setUser does not do any asynchronous stuff - it simply sets user object in state. But you could wrap it in action if you want.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

JFGHT picture JFGHT  路  5Comments

Pixelime picture Pixelime  路  5Comments

amesas picture amesas  路  5Comments

SonarBeserk picture SonarBeserk  路  4Comments

drumanagh picture drumanagh  路  3Comments