I have a question about VueJS and the form of authentication it has, I am trying to perform a multi auth guard with beforeEnter but it does not work, I leave a job on how I want to do it to see if they can help me.
const isGlobal = (to, from, next) => {
console.log('isGlobal called');
if (store.getters.isAuthenticated && store.getters.getProfile.is_global) {
next();
return
}
next(false )
}
const isAdmin = (to, from, next) => {
console.log('isAdmin called');
if (store.getters.isAuthenticated && store.getters.getProfile.is_admin) {
next();
return
}
next(false)
}
const isSupervisor = (to, from, next) => {
console.log('isSupervisor called');
if (store.getters.isAuthenticated && store.getters.getProfile.is_supervisor) {
next();
return
}
next(false)
}
const routes = [{
path: '/',
name: 'login',
component: Login,
beforeEnter: [isSupervisor || isGlobal || isAdmin],
}
];
Thank you
The problem is that [isSupervisor || isGlobal || isAdmin] is an array equals to [false] or [true], it must be a function. Try something like this:
const isGlobal = store.getters.isAuthenticated && store.getters.getProfile.is_global
const isAdmin = store.getters.isAuthenticated && store.getters.getProfile.is_admin
const isSupervisor = store.getters.isAuthenticated && store.getters.getProfile.is_supervisor
const conditionalNext = function(condition) {
return (to, from, next) => {
if (condition) {
next();
return
}
next(false)
}
}
const routes = [{
path: '/',
name: 'login',
component: Login,
beforeEnter: conditionalNext(isSupervisor || isGlobal || isAdmin)
}
];
Related
I am checking if jwt token is expired or not in my route guard. But it is running in an infinite loop. I can't understand why it is not working. Here are my codes:
Route Guard
const parseJwt = (token) => {
const base64Url = token.split('.')[1]
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/')
const jsonPayload = decodeURIComponent(Buffer.from(base64, 'base64').toString('ascii').split('')
.map(c => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)).join(''))
return JSON.parse(jsonPayload)
}
Router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
// need to login
if (localStorage.getItem('token') === null) {
next({
path: '/login',
params: { nextUrl: to.fullPath }
})
} else if (localStorage.getItem('token') && (parseJwt(localStorage.getItem('token')).exp < Date.now() / 1000)) {
console.log('token expired')
next({
path: '/login',
params: { nextUrl: to.fullPath }
})
} else {
next()
}
} else {
if (localStorage.getItem('token') && to.name === 'Login') {
return next({ path: '/' })
}
return next()
}
})
Only Login has requiresAuth as true in my routes.js.
{
path: '/login',
name: 'Login',
component: () => import('pages/auth/Login.vue'),
meta: { requiresAuth: false }
}
I cannot understand this part of code is returning an infinite loop:
next({
path: '/login',
params: { nextUrl: to.fullPath }
})
Please any help is very much appreciated.
Thank you
I think the problem is here
if (localStorage.getItem('token') && to.name === 'Login') {
return next({ path: '/' })
}
Let's assume I have the token but it's expired. The navigation guard will redirect me to login page, which will redirect me to the home page (because it only checks the token existence and thinks i'm authenticated), which will once again redirect me to login page... And here is our infinite loop
So you should also check if the token has expired in this condition:
const token = localStorage.getItem('token')
const isExpiredToken = parseJwt(token).exp < Date.now() / 1000
if (token && !isExpiredToken && to.name === 'Login') {
return next({ path: '/' })
}
Getting this error as I want to check in router.beforeEach if there is a sessionToken already in storage and if not then redirect to Login where I could retrieve it:
Detected an infinite redirection in a navigation guard when going from "/" to "/login". Aborting to avoid a Stack Overflow. This will break in production if not fixed.
My code in router.js
router.beforeEach((to, from, next) => {
if(ENV == 'development') {
let sessionStorage = storage.sessionStorageGet('_sessionToken')
if (sessionStorage === null) next({ name: 'Login' })
else next()
}
})
const routes = [
{
path: '/login',
name: 'Login',
component: () => import('../views/login'),
meta: {
requiresAuth: false
}
},
{
path: '/private',
... private route config,
meta: {
requiresAuth: true
}
}
];
router.beforeEach(async (to, from, next) => {
if (ENV == 'development') {
if (to.matched.some(record => record.meta.requiresAuth)) {
const sessionStorage = storage.sessionStorageGet('_sessionToken')
if (sessionStorage) {
next();
} else {
router.push({ name: 'Login' });
}
} else {
next();
}
}
});
I have a vue-router that looks like this:
const router = new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
children: [
{
{
path: 'main',
name: 'main',
component: () => import(/* webpackChunkName: "main" */ './views/main/Main.vue'),
children: [
{
path: 'dashboard',
name: 'main-dashboard',
component: () => import(/* webpackChunkName: "main-dashboard" */ './views/main/Dashboard.vue'),
},
...
There are route guards in place so that once a user is logged in they are directed to /BASE_URL/main/dashboard.
public beforeRouteEnter(to, from, next) {
routeGuardMain(to, from, next);
}
public beforeRouteUpdate(to, from, next) {
routeGuardMain(to, from, next);
}
const routeGuardMain = async (to, from, next) => {
if (to.name === 'main') {
next({ name: 'main-dashboard'});
} else {
next();
}
};
I'm storing user_id and account_id in a Vuex state and I'd like to be able to create a url structure like:
BASE_URL/<account_id>/dashboard
But I'm having trouble accessing the account_id from the store (I have getters setup to get the relevant params) and passing it as a parameter during the redirect in the route guard (its null / undefined, so I think I need to await somewhere??).
I can set up dynamic urls for paths which don't have a route guard, but not sure how to do it with them in place.
I've read through the vue-router docs, but can't work it out.
Please can anyone suggest how I can achieve the target url structure? Apologies my frontend skills are lacking and I'm new to Vue.js
Thank you!
Found a solution similar to this link:
Accessing Vuex state when defining Vue-Router routes
const startRouteGuard = async (to, from, next) => {
await dispatchCheckLoggedIn(store);
if (readIsLoggedIn(store)) {
if (to.path === '/login' || to.path === '/') {
if (store.getters.userMembership.account_id === null) {
const watcher = store.watch(store.getters.userMembership.account_id, account_id => {
watcher(); // stop watching
next({ name: 'main', params: { account_id: account_id}});
});
} else {
const account_id = store.getters.userMembership.account_id;
next({ name: 'main', params: { account_id: account_id}});
}
} else {
next();
}
} else if (readIsLoggedIn(store) === false) {
if (to.path === '/' || (to.name as string).startsWith('main')) {
next({name: 'login'});
} else {
next();
}
}
};
I am trying to protect a route using beforeEnter. my route looks like such;
path: '/account',
name: 'account',
component: Account,
beforeEnter:
(to, from, next) => {
const authService = getInstance();
const fn = () => {
// If the user is authenticated, continue
if (authService.isAuthenticated) {
console.log('no')
return next();
}
// Otherwise, log in
console.log('should login')
authService.loginWithRedirect({ appState: { targetUrl: to.fullPath } });
};
if (!authService.loading) {
return fn();
}
authService.$watch("loading", loading => {
if (loading === false) {
return fn();
}
})
}
},
THIS functions as I expect, but I don't believe the logic should into the routes file, so simply enough I store it in a different file under my auth folder. Like so;
import { getInstance } from "./index";
export const authGuard = (to, from, next) => {
console.log('test')
const authService = getInstance();
const fn = () => {
// If the user is authenticated, continue with the route
if (authService.isAuthenticated) {
console.log('no')
return next();
}
// Otherwise, log in
console.log('should login')
authService.loginWithRedirect({ appState: { targetUrl: to.fullPath } });
};
// If loading has already finished, check our auth state using `fn()`
if (!authService.loading) {
return fn();
}
// Watch for the loading property to change before we check isAuthenticated
authService.$watch("loading", loading => {
if (loading === false) {
return fn();
}
});
};
However when I import this to my routes and do;
import { authGaurd } from './auth/authGaurd'
Vue.use(Router)
export default new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/account',
name: 'account',
component: Account,
beforeEnter: authGaurd
},
this no long works? I'm sure I must be missing something simple? Any help would be much appreciated.
Try:
- beforeEnter: authGaurd
+ beforeEnter(to, from, next) {
authGaurd(to, from, next)
}
I wonder how to disable a route in VueRouter conditionally, so that it can't be accessed anymore!
I tried to redirect with this.$router.replace('/') but the URL did show the route that I wanted to skip.
Any thoughts?
EDIT:
this is my VUEX-Store: Have a look at router.replace('/')
const store = new Vuex.Store({
state: {
users: [ ],
friendships: [ ],
userID: null
},
mutations: {
signUp(state, payload) {
auth.createUserWithEmailAndPassword(payload.email, payload.password).then((user) => {
if (user !== null) {
state.userID = user.uid
router.replace('/')
}
else {
state.userID = null
}
})
},
signIn(state, payload) {
auth.signInWithEmailAndPassword(payload.email, payload.password).then((user) => {
if (user !== null) {
state.userID = user.uid
router.replace('/')
}
else {
state.userID = null
}
})
},
signOut(state) {
auth.signOut()
state.userID = null
router.replace('/signin')
},
authenticate(state) {
auth.onAuthStateChanged((user) => {
if (user !== null) {
state.userID = user.uid
router.replace('/')
}
else {
state.userID = null
}
})
}
},
actions: {
signUp({ commit }) {
commit('signUp')
},
signIn({ commit }) {
commit('signIn')
},
signOut({ commit }) {
commit('signOut')
},
authenticate({ commit }) {
commit('authenticate')
},
redirect({ commit }) {
commit('redirect')
}
}
})
and here is my component:
<template>
<div id="you">
<h1>you</h1>
<p>You are on your secret page!</p>
<p>{{ $store.state.userID }}</p>
</div>
</template>
<script>
export default {
name: 'you',
beforeCreate() {
if (this.$store.state.userID === null) {
this.$router.replace('/signin')
}
}
}
</script>
You can add a meta feild to that route you want to conditionally disable it like this:
export const routes = [
{path: '/', component: foo},
{path: '/bar', component: bar, meta:{conditionalRoute:true}}
];
And use router.beforeEach in your main.js :
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.conditionalRoute)) {
// this route requires condition to be accessed
// if not, redirect to home page.
if (!checkCondition) {
//check codition is false
next({ path: '/'})
} else {
//check codition is true
next()
}
} else {
next() // make sure to always call next()!
}
})
Or else use beforeRouteEnter() navigation guard locally on that route's component
beforeRouteEnter(to, from, next){
next(vm => {
// access to component instance via `vm`
if(checkCondition){
next();
}else{
next('/');
}
})
}
In your signin component
beforeRouteEnter(to, from, next){
next(vm => {
// access to component instance via `vm`
if(vm.$store.state.userUID !== null){
next('/');
}else{
next();
}
})
}
In your route, you can use navigation guard to check if the route matches the route you want to disable and then return instead of executing next()
router.beforeEach(to, from, next) {
if (to.path === "yourRoute") {
return;
}
}