Vuex autologin with navigation guard - vue.js

Hi im working with vue and vuex trying to set a navigation route so that the user is logged in automatically in case the token is still in local storage. Im getting a rare behaviour, if I check the value of a the getter that returns the variable called isauthenticated which is true if there is a valid token I can clearly see that the value is true, but when I get that value by it self it says its false.
router.beforeEach(function (to, from, next) {
console.log(to);
console.log(from);
console.log(store.getters);
console.log("is auth "+store.getters["auth/isAuthenticated"])
if (to.meta.requiresAuth && !store.getters["auth/isAuthenticated"]) {
console.log("redirected to landing page")
next('/');
} else if (to.meta.requiresUnauth && store.getters["auth/isAuthenticated"]) {
console.log("redirected to home");
console.log(to.meta.requiresUnauth);
next('/home');
} else {
console.log("nexting")
next();
}
});
output:
{}
auth/isAuthenticated: true
auth/token: "eyJhbGciOiJSUz...."
auth/userId: "nHSQ3...."
user/username: ""
get auth/isAuthenticated: ƒ ()
get auth/token: ƒ ()
get auth/userId: ƒ ()
get user/username: ƒ ()
__proto__: Object
is auth false
nexting

the problem was the navigation before each was running before my created() hook in my app.vue I fixed it by doing this:
store.dispatch('auth/tryLogin').then(
router.beforeEach(function (to, from, next) {
if (to.meta.requiresAuth && !store.getters["auth/isAuthenticated"]) {
console.log("redirected to landing page")
next('/');
} else if (to.meta.requiresUnauth && store.getters["auth/isAuthenticated"]) {
console.log("redirected to home");
next('/home');
} else {
console.log("nexting")
next();
}
}));
You can read more about it here Router beforeEach guard executed before state loaded in Vue created()

Related

Vue Router Navigation Guards

I have a page that can´t be accessed without permission. The permission is loaded by axios request in an action in the store. After the request the permission is stored in a store module. In the Navigation Guard beforeEach I have a getter that gets the permissions data from the store module.
Because it did not work I wrote a console.log to log the permissions data. The permissions data is an Array and when it logs the length of the Array it logs 0. That doesn´t make sense, because when I see into the Vue DevTools the store says that the array length is 1.
Does anyone have a solution that the store is faster?
Navigation Guard:
router.beforeEach(async (to, from, next) => {
var hasPermission = await store.getters.availableAppPermissions
hasPermission.forEach(function(item) {
if (
to.path.includes(item.appUrl) &&
to.matched.some(record => record.meta.requiresPermission)
) {
next({ name: 'Home' })
}
})
next()
})
Store Module:
import axios from 'axios'
export default {
state: {
availableApps: []
},
mutations: {
SET_AVAILABLE_APPS(state, availableApps) {
state.availableApps = availableApps
state.permissions = true
}
},
actions: {
loadAppsAvailableForCurrentUser({ commit }) {
return axios.get('/v1/apps').then(data => {
// Filter out apps that have false set in show_in_menu
const filteredApps = data.data.filter(app => app.showInMenu)
commit('SET_AVAILABLE_APPS', filteredApps)
})
}
},
getters: {
availableApps(state) {
return state.availableApps
},
availableAppPermissions(state) {
return state.availableApps.filter(item => item.hasPermission == false)
}
}
}
Code where loadAppsAvailableForCurrentUser is called:
This created is in the NavBar Component it is called on every Site because this Component is in the App.vue
created() {
if (this.$store.getters.loggedIn) {
this.$store.dispatch('loadUserData')
this.$store.dispatch('loadUserImageBase64')
this.$store.dispatch('loadVisibleTabs')
this.$store.dispatch('loadAppsAvailableForCurrentUser')
}
}

vue-router next method not working always in beforeach

i'm creating a navigation guard for redirect user to some page,
i get auth status from vuex:
state: {
auth: false,
},
and in vue-router beforeach, i set a condition that when auth state is false and route is not adminAuth,
redirect to adminAuth route
var auth = store.state.auth
if (!auth){
if( to.name !== "adminAuth" ){
next( { name: 'adminAuth' } )
}
}
problem is when route changed first time, next() not working properly, but second time, working properly!
can you help me?
You want to intercept the navigation request using beforeResolve or beforeEnter because beforeEach is too soon and some attributes of the logic you want to use have not been processed yet, so it is possible on second navigation that some underlying logic appears resolved because the next request is accessing something set by the previous request.
const router = new VueRouter({
routes: { ... }
})
router.beforeResolve((to, from, next) => {
if(!router.app.$store.state.auth && to.name !== 'adminAuth') {
next( { name: 'adminAuth' } )
}
next()
})

Vue router guard beforeEach store user location before login and redirect after. What is right way to implement?

I'm using vue router with quasar framework. Here is my route guard at beforeEach hook:
let storedURL = null
router.beforeEach((to, from, next) => {
const record = to.matched.find(record => record.meta.auth)
if (record) {
if (store.getters['auth/check']) {
if (storedURL) {
const redirectURL = storedURL
storedURL = null
next(redirectURL)
} else {
next()
}
} else {
storedURL = to.path
next('/')
}
} else {
if (to.path === '/' && store.getters['auth/check']) {
next('/dashboard')
} else {
next()
}
}
})
It works but throws an error when store location:
Error: Script terminated by timeout at:
notify#webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:730:36
reactiveSetter#webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:1056:11
proxySetter#webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:4625:5
__onControlFocusin#webpack-internal:///./node_modules/quasar/src/components/field/QField.js:345:9
invokeWithErrorHandling#webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:1853:26
invoker#webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:2178:14
add$1/original._wrapper#webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:6907:25
and when redirects:
uncaught exception: undefined
Without this guard there are no errors. But I can't understand what is wrong with my code. Thanks for help!

vue router next() function don't work in Promise in router.beforeEach

I have the following code:
router.beforeEach((to, from, next) => {
if (to.name !== from.name) {
store
.dispatch("fetchCurrentUser")
.then(() => {
console.log('then');
// do something
next();
})
.catch(() => {
console.log('catch');
router.push("/login");
next();
});
} else {
next();
}
// next();
});
I'm trying to get the current user, and if this succeeds, then do something with this data, and if the request is not successful, then redirect the user to the login page. But next () calls do not work, I get the "then" or "catch" in the console, but the redirect does not occur and an infinite loop begins. But if I take next () from condition (commented row) the redirect works fine.
To redirect you should use next('/') or next({ path: '/' }).
From the documentation:
next: Function: this function must be called to resolve the hook. The
action depends on the arguments provided to next:
next(): move on to the next hook in the pipeline. If no hooks are
left, the navigation is confirmed.
next(false): abort the current navigation. If the browser URL was
changed (either manually by the user or via back button), it will be
reset to that of the from route.
next('/') or next({ path: '/' }): redirect to a different location.
The current navigation will be aborted and a new one will be started.
You can pass any location object to next, which allows you to specify
options like replace: true, name: 'home' and any option used in
router-link's to prop or router.push
The promise resolves after the function ends.
This means that the commented next happens regardless of the result of the promise result. Then the promise resolves and you call another next.
The bottom line is that you don't need the commented next and should just cover the promise resolve.
I was able to implement an async validation inside beforeEach, authentication in my case.
export async function onBeforeEach(to, from, next) {
let { someUserToken } = to.query
if (someUserToken) {
let nextRoute = await authenticate(to)
next(nextRoute)
} else {
const userToken = store.state.application.userToken
if (!to.meta.public && !userToken) {
next({ name: 'Forbidden' })
} else {
next()
}
}
}
async function authenticate(to) {
let { someUserToken, ...rest } = to.query
return store
.dispatch('application/authenticate', {
someUserToken
})
.then(() => {
return {
name: 'Home',
query: {
...rest
}
}
})
.catch(() => {
return { name: 'Forbidden' }
})
}
I hope this helps.

vue.js: Route guard wait for async value

As in every application I have a few routes. For example (router/index.js excerpt):
[{
path: '/reporting',
name: 'Reporting',
component: reporting,
meta: {
adminOnly: true
}
},
...]
As you can see in the route definition, when accessing reporting the user needs to have admin permissions, which is a property in a vuex store. The problem: This property is async and is of course false when initially accessing in the guard. How can I make my guard wait for it?
Guard:
if (to.matched.some(route => route.meta.adminOnly)) {
if (store.getters.userInfo.isAdmin) {
next()
} else {
next('/')
}
} else {
next()
}
What about watching the getter, using store.watch?
Assuming that your getter returns null if the data is not fetched yet.
if (store.getters.userInfo.isAdmin === null) {
const watcher = store.watch(store.getters.userInfo.isAdmin, isAdmin => {
watcher(); // stop watching
if (isAdmin) next();
else next('/');
});
}
else if (store.getters.userInfo.isAdmin) next();
else next('/');