Basically, im creating users with a default password and a hasDefaultPassword boolean property set to true and after the login screen i want to force users to change the password and set hasDefaultPassword to false.
Most of this is working, i login and the Change password screen shows up:
async function login() {
if (await validateLoginForm()) {
let user = {
email: email.value,
password: password.value
}
ApiHelper.post('user/login', user)
.then(async (response) => {
if (response.status == 200) {
authStore.saveToken(response.data.userToken, response.data.userForLogin);
if(authStore.user.hasDefaultPassword){
router.push({ name: 'changePassword' });
}else{
router.push({ name: 'home' });
}
}
}).catch((e) => {
loginErrorMessage.value = e.status == 404 ? e.response.data.message : 'An error occurred. Try again later.';
})
}
}
The problem is that its letting me navigate to the other routes, which is something i want to prevent until the password is changed, and i cant figure out how to handle that in the router beforeEach:
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: '/login',
name: 'login',
component: Login,
meta: {
all: true
}
},
{
path: '/changePassword',
name: 'changePassword',
component: ChangePassword,
meta: {
admin: true,
storage: true,
sales: true
}
},
{
path: '/',
name: 'home',
component: Home,
meta: {
admin: true,
storage: true,
sales: true
}
},
{
path: '/category',
name: 'category',
component: Category,
meta: {
admin: true,
storage: true
}
},
{
path: '/user',
name: 'user',
component: User,
meta: {
admin: true
}
},
{
path: '/:pathMatch(.*)',
name: '404',
component: PageNotFound,
meta: {
all: true
}
}
]
})
router.beforeEach(async (to, from, next) => {
const authStore = useAuthStore();
if (to.matched.some(record => record.meta.all)) {
next();
} else if (authStore.user && authStore.user.role.userTypeDescription == 'Admin') {
if (to.matched.some(record => record.meta.admin)) {
next();
}
} else if (authStore.user && authStore.user.role.userTypeDescription == 'Sales') {
if (to.matched.some(record => record.meta.sales)) {
next();
}
} else if (authStore.user && authStore.user.role.userTypeDescription == 'Storage') {
if (to.matched.some(record => record.meta.storage)) {
next();
}
} else {
next({ name: 'login' })
}
})
Everything i have tried so far, which was mostly changing the ifs and adding different conditions, breaks the router, causing an infinite loop or an error saying that the next callback was never called.
Heres an example. I changed the route meta and added this else if condition and when i login, it takes me to either the home or the change password screen, depending on the value of hasDefaultPassword, which is correct:
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: '/login',
name: 'login',
component: Login,
meta: {
all: true
}
},
{
path: '/changePassword',
name: 'changePassword',
component: ChangePassword,
meta: {
changePassword: true
}
},
{
path: '/',
name: 'home',
component: Home,
meta: {
admin: true,
storage: true,
sales: true
}
},
{
path: '/category',
name: 'category',
component: Category,
meta: {
admin: true,
storage: true
}
},
{
path: '/user',
name: 'user',
component: User,
meta: {
admin: true
}
},
{
path: '/:pathMatch(.*)',
name: '404',
component: PageNotFound,
meta: {
all: true
}
}
]
})
router.beforeEach(async (to, from, next) => {
const authStore = useAuthStore();
if (to.matched.some(record => record.meta.all)) {
next();
} else if (authStore.user && authStore.user.hasDefaultPassword) {
if (to.matched.some(record => record.meta.changePassword)) {
next();
}
} else if (authStore.user && authStore.user.role.userTypeDescription == 'Admin') {
if (to.matched.some(record => record.meta.admin)) {
next();
}
} else if (authStore.user && authStore.user.role.userTypeDescription == 'Sales') {
if (to.matched.some(record => record.meta.sales)) {
next();
}
} else if (authStore.user && authStore.user.role.userTypeDescription == 'Storage') {
if (to.matched.some(record => record.meta.storage)) {
next();
}
} else {
next({ name: 'login' })
}
})
BUT if im in the home or the change password screen and i try to navigate to the other, i get this error:
Any ideas?
Thanks!
The problem was that there were some cases that i wasnt handling, like an admin visiting a route that isnt for admins, such as changePassword:
if (authStore.user.role.userTypeDescription == 'Admin') {
if (to.matched.some(record => record.meta.admin)) {
next();
} else {
//Im an admin but im visiting a route that doesnt have the admin meta
}
}
I solved it by handling such cases, sending users to a 404 page for the time being:
router.beforeEach(async (to, from, next) => { // TODO -> Allow all users to change their passwords
const authStore = useAuthStore();
if (to.matched.some(record => record.meta.all)) {
next();
} else if (authStore.user) {
if (authStore.user.hasDefaultPassword) {
if (to.matched.some(record => record.meta.changePassword)) {
next();
} else {
next({ name: 'changePassword' })
}
} else {
if (authStore.user.role.userTypeDescription == 'Admin') {
if (to.matched.some(record => record.meta.admin)) {
next();
} else {
next({ name: '404' })
}
} else if (authStore.user.role.userTypeDescription == 'Sales') {
if (to.matched.some(record => record.meta.sales)) {
next();
} else {
next({ name: '404' })
}
} else if (authStore.user.role.userTypeDescription == 'Storage') {
if (to.matched.some(record => record.meta.storage)) {
next();
} else {
next({ name: '404' })
}
}
}
} else {
next({ name: 'login' })
}
})
I feel like the code could be a lot cleaner, and now that i think about it, i probably should force users with the default password to change it, but also allow other users to change it if they want to.