Vue SPA - check if user is authenticated, redirect to login if not

Viewed 1547

I have a VueJS single-page application and I use JWT authentication.

I'm trying to figure out how to make sure that User is authenticated after page reload and if not, redirect them to login page.

accessToken and refreshToken are stored in the cookies and also in Vuex

Vuex.state:

auth: {
        user: {},
        isAuthenticated: false,
        accessToken: null,
        refreshToken: null
    },

Vuex.actions.refreshToken

    refreshToken: async ({state, commit, dispatch}) => {
        try {
            await api.jwtRefresh(state.auth.refreshToken).then(response => {
                if (response.status === 200) {
                    dispatch("setAuthData",{
                        accessToken:response.data.access,
                        isAuthenticated:true
                    })
                }
            }).catch(err => {
                dispatch('logout')
            });
        } catch (e) {
            dispatch('logout')
        }
    },

App.vue

export default {
  data: () => ({}),
  mounted() {
    this.$store.dispatch('setAuthDataFromCookies')
    this.$store.dispatch('refreshToken') // checks if user is authenticated, redirect to login page if not
    this.$router.push('/dashboard')
  }
}

My idea is to try to refresh the JWT token. If it was successfully refreshed User can proceed to /dashboard. If not, User is redirected to the /login

The problem is that mounted doesn't wait until refreshToken is done and it redirects User immediately to the /dashboard even before token is refreshed.

How can I make it wait? (The idea is that refreshToken will redirect user to /login in case of error.

1 Answers

You can setup a meta auth field in your router, and a global beforeEnter or beforeEach guard that checks Vuex (or your cookies, or both) for a token.

In your router.js file you'd have something like

routes: [
  {
    name: 'Login'
  },
  {
    name: 'Dashboard', // + path, component, etc
    meta: {
      auth: true
    }
  }
]

Then you setup a global guard, something like this:

router.beforeEach((to, from, next) => {
  if (to.matched.some(record => record.meta.auth)) {
    if (!store.getters.authToken) {
      next({ name: 'Login' });
    } else {
      next();
    }
  } else {
    next(); // Very important to call next() in this case!
  }
})

This will check before each and every route transition whether the next route has the auth meta field. If it does, it checks your Vuex state for the token, and otherwise navigates as normally.

Vue Router Docs on Navigation Guards

In your case, you're trying to authenticate the user, so you can just call your endpoint inside of the beforeEach guard and redirect like that based on the response. Just make sure to make the callback asynchronous, like router.beforeEach(async (to, from, next) => {})

Related