Vue-Router beforeEnter not function as expected? - vue.js

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)
}

Related

Return component in beforeEnter in vue router

I have the following path in my router
{
path: '/Page',
component: async () => {
const isUserLogged = await getUser()
const store = useStore()
if (userLogged && store.value1) {
return import('./pages/PublicPage/PublicPage.vue')
} else {
return import('./pages/NonPublicPage/NonPublicPage.vue')
}
},
},
}
Every time I enter this path, I need to return a different component depending on the value in the store, but the component is loaded only once.
I tried to rewrite the structure so that it uses beforeEnter as follows:
{
path: '/Page',
beforeEnter: async (to, from, next) => {
const isUserLogged = await getUser()
const store = useStore()
if (userLogged && store.value1) {
next({
component: () => import('./pages/PublicPage/PublicPage.vue'),
})
} else {
next({
component: () => import('./pages/NonPublicPage/NonPublicPage.vue'),
})
}
},
}
But this solution doesn't work. Without using a different path, I need to return a different component depending on the conditions. Is it possible to return a component in beforeEnter in next() or is there another solution to this problem?

use firebase auth with vue 3 route guard

I have the needings to use firebase auth with vue router.
I have this simple guard, but I've noticed that sometimes the users will see for a while the pages also if they are not logged.
router.beforeEach( async (to, from) => {
onAuthStateChanged( getAuth(app), (user) => {
console.log(user, to.meta.requireAuth)
if( to.meta.requireAuth && !user ) {
return {
name: 'Signin'
}
}
})
})
I also have this kind of control inside my components, but I'm looking for something global to use to prevent unregistered users to see the app.
Any suggestion?
You can wrap the onAuthStateChanged in a Promise and make your before each an async function.
// in some global file
export async function getCurrentUser(): Promise<User | null> {
return new Promise((resolve, reject) => {
const unsubscribe = auth.onAuthStateChanged((user) => {
unsubscribe();
resolve(user);
}, reject);
});
}
// your router file
router.beforeEach(async (to, from, next) => {
if (to.matched.some((record) => record.meta.publicAccess)) {
next();
} else {
const currentUser = await getCurrentUser();
if (currentUser) {
next();
} else {
next({ name: "Login" });
}
}
});
// Your route object
{
name: "Login",
path: "/login",
component: () => import("#/views/authentication/Login.vue"),
}

How to get around vue router infinite redirection error?

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();
}
}
});

Dynamic root url structure in Vue with vue-router, route guard & Vuex

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();
}
}
};

Why does my Vue Router throw a Maximum call stack error?

I have a really simple routing practically looks like this I'm using this under electron
import Vue from "vue";
import VueRouter from "vue-router";
import Projects from "../views/Projects.vue";
import RegisterUser from "#/views/RegisterUser.vue";
//import { appHasOwner } from "#/services/";
Vue.use(VueRouter);
const routes = [
{
path: "/",
name: "projects",
component: Projects,
meta: {
requiresUser: true
}
},
{
path: "/register",
name: "register",
component: RegisterUser
},
{
path: "/settings",
name: "settings",
meta: {
requiresUser: true
},
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () =>
import(/* webpackChunkName: "about" */ "../views/Settings.vue")
}
];
const router = new VueRouter({
mode: "history",
base: process.env.BASE_URL,
routes
});
router.beforeEach((to, from, next) => {
if (to.matched.some(route => route.meta.requiresUser === true)) {
//this will be for test case undefined
let user;
if (typeof user === "undefined") {
// eslint-disable-next-line no-console
console.log(user); //logs undefined but at the end no redirect
next("/register");
} else {
next();
}
}
});
export default router;
taking the example from the docs
// GOOD
router.beforeEach((to, from, next) => {
if (!isAuthenticated) next('/login')
else next()
})
the application can boot only if there is a user attached in database either should redirect to the register component but the code above will end with Maximum call stack size exceeded. So how to check with beforeEach conditions end redirect to a given page?
The Maximum call stack size exceeded is usually due to infinite recursion, and that certainly seems to be the case here. In router.beforeEach you're calling next to go to the /register route, which goes back into this method, which calls next, and so on. I see you have a requiresUser in your meta, so you need to check that in beforeEach, like this:
router.beforeEach((to, from, next) => {
// If the route's meta.requiresUser is true, make sure we have a user, otherwise redirect to /register
if (to.matched.some(route => route.meta.requiresUser === true)) {
if (typeof user == "undefined") {
next({ path: '/register' })
} else {
next()
}
}
// Route doesn't require a user, so go ahead
next()
}