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
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)
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
I don't feel comfortable when see direct access to state instead of using getters 🙂
You could just replaced that line with a getter 😜
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?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.
What did you use to create the flow logic