DEV Community

Cover image for Vue SPA, router guard with redirect after login
Harry Gill
Harry Gill

Posted on • Updated on

Vue SPA, router guard with redirect after login

Originally published on my blog

While making Let's Organise SPA(Single page Application) we 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');
  }
};

Say hello if you like on Twitter

Top comments (6)

Collapse
 
inizio profile image
IniZio

I think it would be better to store the entryUrl in url query though, because otherwise when the user refreshes the login page, entryUrl will be null again

Collapse
 
denisinvader profile image
Mikhail Panichev

I don't feel comfortable when see direct access to state instead of using getters 🙂

Collapse
 
mygnu profile image
Harry Gill

You could just replaced that line with a getter 😜

Collapse
 
denisinvader profile image
Mikhail Panichev • Edited

It seems a little complicated.

What if store initialUrl in vuex (/ by default), set it to first requested url and just redirect to this url after successful login?

Collapse
 
mygnu profile image
Harry Gill

That was my first implementation, problem with that is if the user clicks on a link to the app from an email perhaps. I wanted to serve the page if logged in, or remember the link and redirect to login. After that are done, the logic above would serve the desired page.

Collapse
 
skngetich5 profile image
Stephen Ng'etich

What did you use to create the flow logic