Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Access Vuex before Vue route is resolved

What do I have:

  1. router with auth-required routes and public routes
  2. vuex with user's auth state

What do I want: send request to server using axios to check user's auth state before app is loaded (before router is resolved).

router.js:

import Vue from 'vue'
import Router from 'vue-router'
import store from './store'

Vue.use(Router)

const router = new Router({
  ...
  routes: [
    {
      path: '/',
      name: 'home',
      component: Home
    },
    {
      path: '/account',
      name: 'account',
      component: () => import(/* webpackChunkName: "account" */ './views/Account.vue'),
      meta: {
        requiresAuth: true
      }
    }
  ]
})

router.beforeEach((to, from, next) => {
  if (to.matched.some(route => route.meta.requiresAuth)) {
    if (store.state.authStatus)
      next()
    else
      next({name: 'home'})
  } else {
    next()
  }
})

export default router

store.js:

import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'

Vue.use(Vuex)

const store = new Vuex.Store({
  state: {
    authStatus: false
  },
  mutations: {
    setAuthStatus(state, authStatus) {
      state.authStatus = authStatus
    }
  }
})

 axios.get(...)
   .then(response => {
     store.commit('setAuthStatus', true)
   })

export default store

main.js:

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

My problem: when I enter mydomain.com/acount in browser (app is not loaded before) while being authorized, anyway I got redirect to home. And after redirect I see that I'm authorized (I set up some DOM element inside Home component which is shown only for authorized users).


I've tried this, didn't helped:

store.js:

const store = new Vuex.Store({
  ...
  actions: {
    setAuthStatus({commit}) {
      axios.get(...)
        .then(response => {
          commit('setAuthStatus', true)
        })
    }
  }
})

main.js:

store.dispatch('setAuthStatus').then(() => {
  new Vue({
    router,
    store,
    render: h => h(App)
  }).$mount('#app')
})

Edit: in main.js I tried to go from

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

to

new Vue({
  store,
  router,
  render: h => h(App)
}).$mount('#app')

and it also didn't helped.

like image 224
Tarasovych Avatar asked Sep 20 '18 08:09

Tarasovych


2 Answers

Going around Vanojx1's answer, I solved my question next way.

store.js:

const store = new Vuex.Store({
  state: {
    authStatus: axios.get(...).then(response => {
      store.commit('setAuthStatus', true)
    }),
    userAuth: false
  },
  mutations: {
    setAuthStatus(state, authStatus) {
      state.userAuth = authStatus
    }
  }
})

router.js:

router.beforeEach((to, from, next) => {
  if (to.matched.some(route => route.meta.requiresAuth)) {
    store.state.authStatus.then(() => {
      //we're getting 200 status code response here, so user is authorized
      //be sure that API you're consuming return correct status code when user is authorized
      next()
    }).catch(() => {
      //we're getting anything but not 200 status code response here, so user is not authorized
      next({name: 'home'})
    })
  } else {
    next()
  }
})
like image 114
Tarasovych Avatar answered Oct 08 '22 01:10

Tarasovych


In the navigation guard you need something async so save your axios promise in the store as authStatus. When it resolves commit and set the loggedIn status. In the navigation guard wait for the promise to be resolved and then call the next function to enter the next router.

Store.js

import Vue from "vue";
import Vuex from "vuex";

Vue.use(Vuex);

const store = new Vuex.Store({
  state: {
    /*  EXAMPLE
    authStatus: new Promise(resolve => {
      setTimeout(() => {
        const requestResult = true;
        store.commit("setAuthStatus", requestResult);
        resolve(requestResult);
      }, 1000);
    }),
    */
    authStatus: axios.get(...).then((requestResult) => {
        store.commit("setAuthStatus", requestResult);
    }),
    loggedIn: false
  },
  mutations: {
    setAuthStatus(state, loggedIn) {
      state.loggedIn = loggedIn;
    }
  }
});

export default store;

router.js

router.beforeEach((to, from, next) => {
  if (to.matched.some(route => route.meta.requiresAuth)) {
    store.state.authStatus.then(loggedIn => {
      if (loggedIn) next();
      else next({ name: "home" });
    });
  } else {
    next();
  }
});

Check this solution working here

like image 43
Vanojx1 Avatar answered Oct 08 '22 00:10

Vanojx1