Vue SPA, router guard with redirect after login

March 18, 2018

Harry Gill
Tags: routing auth login vue vuex | 3min Read

While making Let’s Organise SPA(Single page Application) I bumped into the issue of properly checking Auth and redirecting when a user visited a route that required Authentication. If User is authenticated then go to the requested page or redirect to /login page. Once the user has successfully logged in redirect them back to the requested page.

Vue Router has something called Navigation Guards to the rescue, that you can use as hook before every route or selected routes. Navigation Guard is just a plain function and it works like the following.

function guard(to, from, next){
    if(store.state.auth.loggedIn) {
        // or however you store your logged in state
        next(); // allow to enter route
    } else{
        next('/login'); // go to '/login';
    }
}
...
// later in the guarded routes
export default [{
    path: '/membership',
    beforeEnter: guard, // Using guard before entering the route
    component: require('layouts/membershipLayout').default,
    children: [
      { path: '', component: require('pages/membership').default },
      ...
    ]
  }...
  ]

Code above invokes the guard function at before enter hook and does a basic check and redirection. But really what you need is for the router to remember where user started the route, and also check wether user is already authenticated (maybe in another tab).

Our application uses JWT(JSON Web Token) to authenticate Users. The JWT is not stored with client side JavaScript but in a secure https cookie. This means that the application JavaScript can’t tell wether that token is valid or is even if it exists for that matter. This design requires at least one round trip to the server to validate the token.

In case of User being already authenticated in another tab, redirecting to login page is not a great user experience. So you can think of the flow lik following.

Router flow logic

alt text

We want to execute the logic flow above before every guarded route is visited. We have a place holder variable entryUrl to keep track of url user entered the site with initially set to null. Then we check if we are logged in our application keeps a variable in the Vuex state, If logged in then we check if the entryUrl variable is not set, then simply go to the next route as normal. Otherwise we redirect to that entryUrl and set the variable to null again.

If the Vuex logged in variable not set to true,(this could be if user just visited a url without going through login page) in then we invoke a call to the server to check if user is already authenticated. then check for authentication, if yes go to the route. If every check fails then we store the initial url visited by the user in entryUrl variable and redirect user to ‘login page’.

Corresponding code to the above model looks something like.

// routes.js

import store from '../store';

let entryUrl = null;

const guard = async (to, from, next) => {
  if (store.state.auth.loggedIn) {
    if (entryUrl) {
      const url = entryUrl;
      entryUrl = null;
      return next(url); // goto stored url
    } else {
      return next(); // all is fine
    }
  }

  await store.dispatch('checkAuth'); 
  // we use await as this async request has to finish 
  // before we can be sure

  if (store.state.auth.loggedIn) {
    next();
  } else {
    entryUrl = to.path; // store entry url before redirect
    next('/login');
  }
};