Some of my app routes are restricted for admins only for that I've added requiresAdmin: true, to those routes meta but somehow I doesn't prevent other users of accessing those routes.
Code
PS: I've commented parts for better understanding.
const router = new VueRouter({
mode: "history",
routes: [
// ADMIN ROUTES
{
path: '/export',
name: 'ExportXML',
component: ExportXML,
meta: {
requiresAuth: true,
requiresAdmin: true, // only admins can see this page
layout: 'admin',
name: 'Export XML',
}
},
]
});
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
if (!store.getters.isLoggedIn) {
next({
name: 'login'
})
} else {
next()
}
}
if (to.matched.some(record => record.meta.requiresAdmin)) {
// first make sure getter can get logged user data
if (store.getters.loggedUser && !store.getters.loggedUser === undefined) {
// then check if loged user "type" is admin (any other possebilities are denied)
if (!store.getters.loggedUser.type === 'admin' || store.getters.loggedUser.type === '' || store.getters.loggedUser.type === null || store.getters.loggedUser.type === undefined || store.getters.loggedUser.type === 'worker') {
next({
name: 'dashboard'
})
} else {
next()
}
}
}
else {
next()
}
});
router.afterEach((to, from) => {
Vue.nextTick(() => {
document.title = to.pageTitle || 'Testing Site';
});
});
export default router;
Any idea why for example my user with type of worker still can see exports page while is restricted for admins only?
The issue is here
if (to.matched.some(record => record.meta.requiresAuth)) {
if (!store.getters.isLoggedIn) {
next({
name: 'login'
})
} else {
next() // 👈 specifically here
}
}
This skips any more checks once you've validated that the user is logged in.
You need to move the next if block checking for admins into that else block above, replacing the next(). In fact, you could clean this up by using return to exit the processing when required
if (to.matched.some(({ meta }) => meta.requiresAuth) && !store.getters.isLoggedIn) {
return next({ name: 'login' }) // not logged in, redirect to login
}
if (to.matched.some(({ meta }) => meta.requiresAdmin)
&& store.getters.loggedUser.type !== 'admin') {
return next({ name: 'dashboard' }) // not admin, redirect to dashboard
}
next() // otherwise, everything is fine
check my comment in the code
if (to.matched.some(record => record.meta.requiresAuth)) {
if (!store.getters.isLoggedIn) { // <== problem in condition
next({
name: 'login'
})
} else {
next(). // this line is always executed if a user is logged in. so any any logged in user can visit url in your application
}
}
one possible solution may be
if (to.matched.some(record => record.meta.requiresAuth)) {
if (!store.getters.isLoggedIn) {
next({
name: 'login'
})
} else {
// then check if loged user "type" is admin (any other possebilities are denied)
if (!store.getters.loggedUser.type === 'admin' || store.getters.loggedUser.type === '' || store.getters.loggedUser.type === null || store.getters.loggedUser.type === undefined || store.getters.loggedUser.type === 'worker') {
next({
name: 'dashboard'
})
} else {
next()
}
}
}
else {
next()
}
Related
I am facing this problem when i try to redirect the user to their path depends on their roles_id value
const getUserRole = (to, from, next) => {
const currentUser = JSON.parse(localStorage.getItem("userData"));
if (currentUser) {
if (currentUser.roles_id === 1 && to.name === "user-dashboard") {
next({
name: "dashboard-new"
});
} else if (currentUser.roles_id === 2 && to.name === "dashboard-new") {
next({
name: "user-dashboard"
});
} else if (currentUser.roles_id === 3 && to.name === "mails-track") {
next({
name: "dispatch-dashboard"
});
} else if (to.name === "dashboard-new" && currentUser.roles_id === 1) {
next();
} else if (to.name === "user-dashboard" && currentUser.roles_id === 2) {
next();
} else if (to.name === "dispatch-dashboard" && currentUser.roles_id === 3) {
next();
} else {
next("/");
}
} else {
next("/");
}
};
export default [{
path: "/dashboard",
name: "dashboard-new",
component: () =>
import ("#/views/dashboard/new/DashboardNew.vue"),
meta: {
requiresAuth: true
},
beforeEnter: (to, from, next) => {
const currentUser = JSON.parse(localStorage.getItem("userData"));
if (currentUser && currentUser.roles_id === 1) {
getUserRole(to, from, next);
} else {
next("/");
}
}
},
{
path: "/user-dashboard",
name: "user-dashboard",
component: () =>
import ("#/views/dashboard/new/user/UserDashboard.vue"),
meta: {
requiresAuth: true
},
beforeEnter: (to, from, next) => {
const currentUser = JSON.parse(localStorage.getItem("userData"));
if (currentUser && currentUser.roles_id === 2) {
getUserRole(to, from, next);
} else {
next("/");
}
}
}
];
But I am getting "Maximum call stack size exceeded" i tried to use switch condition and also didn't work
It is working fine on local but once i upload it to my host it show this problem
Is there anything wrong with my code?
After a debugging i found that in local currentUser.roles_id === 1 will return 1 as number but in host will return it as string
so in this case i don't need to specify the type in '===' so i changed it to '==' to check only the value
I am a beginner in vue and I need your help please. I am creating an application where the login is connected to firebase. I would like to use vue-router to redirect a user to a particular page. When a person logs in whose user.role = "admin" it should be redirected to "/admin". Every other logged person to "/" and non-logged in people are redirected to "/login" page.
Here are parts of my code:
main.js
router.beforeEach((to, from, next) => {
const currentUser = firebase.auth().currentUser;
const requiresAuth = to.meta.requiresAuth;
if (requiresAuth && !currentUser){ next({ name: 'Login' })}
else if (!requiresAuth && currentUser) {next({name: 'Dashboard'}), console.log(currentUser)}
else next();
});
authStore.js
const actions = {
logIn({ dispatch,commit,rootGetters }){
firebase.auth().onAuthStateChanged(async (user) => {
if (user) {
commit('SET_USER', user);
var uid = user.uid;
db.collection('users').doc(uid)
.get()
.then((doc)=>{
commit('gutscheinStore/SET_USER_ROLE', doc.data().role, {root:true})
commit('gutscheinStore/SET_USER_STANDORT_ID', doc.data().standortID, {root: true})
commit('gutscheinStore/SET_USER_BETREIBER_ID', doc.data().betreiberID, {root: true})
//console.log(rootGetters['gutscheinStore/getUserRole'])
})
router.push('/')
} else {
console.log("No entry without login")
}
})
},
index.js in router
const routes = [
{
path: '/',
name: 'Dashboard',
component: Dashboard,
meta: {
requiresAuth: true,
}
},
{
path: '/login',
name: 'Login',
component: Login
},
{
path: '/admin',
name:'AdminDashboard',
component: AdminDashboard,
meta: {
requiresAuth: true,
}
In authStore.js where you currently have the line router.push("/"):
const actions = {
...
// snipped for clarity
router.push('/')
} else {
console.log("No entry without login")
}
})
},
You could instead change this as follows:
const actions = {
...
// snipped for clarity
if(user.role === "admin") {
router.push("/admin")
} else {
router.push("/")
}
} else {
// router.push("/login")
router.go()
}
})
},
I've eliminated all the ways of 'next' being called twice to prevent loops. Everything seems to work except what's going on inside the if (authenticated) block. The goal is to keep the user stuck at the RegisterFlow page until they've verified & provided a display name. Where am I going wrong?
Error: Uncaught (in promise) RangeError: Maximum call stack size exceeded
Code:
const authPages = ["LoginPage", "Register", "ForgotPass"];
const publicPages = ["PrivacyPolicy", "Terms"];
router.beforeEach(async (to, from, next) => {
console.log("name", to.name);
if (!store.state.auth.ready) {
try {
await store.dispatch("auth/authenticate");
} catch (err) {
console.log("#router err: ", err);
}
}
const authenticated = store.state.auth.authenticationStatus;
const verified = store.state.auth.verificationStatus;
let displayName = null;
if (store.state.auth.user && store.state.auth.user.displayName) {
displayName = store.state.auth.user.displayName;
}
if (publicPages.includes(to.name) || to.name == "Landing") {
next();
} else if (authenticated) {
if (verified && displayName) {
if (authPages.includes(to.name) || to.name == "RegisterFlow") {
next("/");
} else {
next();
}
} else {
next({ name: "RegisterFlow" });
}
} else if (!authenticated) {
if (authPages.includes(to.name)) {
next();
} else if (to.name == "RegisterFlow") {
next({ name: "LoginPage" });
} else {
next({ name: "LoginPage" });
}
}
});
In this block:
if (verified && displayName) {
if (authPages.includes(to.name) || to.name == "RegisterFlow") {
next("/");
} else {
next();
}
} else {
next({ name: "RegisterFlow" });
}
When someone is not (verified && displayName) you will always trigger a next({name: "RegisteFlow"}) even if to.name == RegisterFlow. This causes the infinite loop. Something like:
if (verified && displayName) {
if (authPages.includes(to.name) || to.name == "RegisterFlow") {
next("/");
} else {
next();
}
} else {
if (to.name == "RegisterFlow") {
next();
} else {
next({ name: "RegisterFlow" });
}
}
will probably do the trick.
I'm trying to use vue-resource inside my router to get info about the user by token to protect some routes:
router/index.js:
const router = new Router({
mode: 'history',
linkExactActiveClass: 'is-active',
routes: [
{
path: '/login',
name: 'Login',
component: Login
},
{
path: '/board',
name: 'Board',
component: Board,
meta: {
requiresAuth: true
}
}
]
})
router.beforeEach((to, from, next) => {
// check if rout requires auth
if (to.matched.some(rec => rec.meta.requiresAuth)) {
const token = localStorage.getItem('user-token')
if (token == null) {
next({ name: 'Login' })
}
else {
this.$http.get('/rest-auth/user/', {headers: {"Authorization": "Token " + token}})
.then(response => { next() }, response => { next({ name: 'Login' }) });
}
}
else {
next()
}
})
But I'm getting error when I'm trying to log in: TypeError: Cannot read property 'get' of undefined, so I've tried to solve it like this to get access to vm instance:
router.beforeEach((to, from, next) => {
// check if rout requires auth
if (to.matched.some(rec => rec.meta.requiresAuth)) {
const token = localStorage.getItem('user-token')
if (token == null) {
next({ name: 'Login' })
}
else {
next(vm => {
vm.$http.get('/rest-auth/user/', {headers: {"Authorization": "Token " + token}})
.then(response => { next() }, response => { next({ name: 'Login' }) });
})
}
}
else {
next()
}
})
But it's not working also, so maybe i need to switch to axios to do it?
I'm pretty sure you need to import vue-resource from 'vue-resource'
When I store the user key in localstorage and redirect the user to the dashboard after successful login my application is not using the stored key until after a refresh.
This is the code that sets the key.
axios.post(url, creds)
.then((response) => {
if (response.data.code === 401) {
context.error = response.data.data
} else {
Vue.ls.set('id_token', response.data.data.key, 60 * 60 * 1000)
this.user.authenticated = true
}
}).catch((err) => {
context.error = err.data
})
The funny thing is that I have a route guard in a beforeEach, this actually uses the right value right after the login, without a refresh.
router.beforeEach((to, from, next) => {
const r = axios.create({
baseURL: env.api_url,
timeout: 25000,
headers: {
'Authorization': 'Bearer ' + Vue.ls.get('id_token'),
'X-Requested-With': 'XMLHttpRequest'
}
})
if (to.name === 'Login') {
r.get('user').then(() => {
next({name: 'Dashboard'})
}).catch(() => {
next({name: 'Login'})
})
}
if (to.name !== 'Login') {
r.get('user').then(() => {
next()
}).catch(error => {
console.log(error)
next({name: 'Login'})
})
} else next()
})
What could be causing this?
Thanks to JacobGoh's comment I managed to find the issue. I created the axios instance in my main.js file. This is where I set the Authorization headers. When a user logs in this is not updated.
I did the following instead:
router.beforeEach((to, from, next) => {
if (Vue.ls.get('id_token') === null && to.name !== 'Login' && to.name !== 'Register') {
router.push('/login')
} else {
next()
}
})
Vue.$http.interceptors.request.use(
config => {
config.headers.Authorization = 'Bearer ' + Vue.ls.get('id_token')
return config
},
error => Promise.reject(error)
)
Works like a charm.