VueJS + VueRouter: Conditionally Disabling A Route - vue.js

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

Related

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 solve the error "Redirected when going from ' / ' to '/dashboard' via a navigation guard"?

When I login with a user, it redirects me to the dashboard as expected. As soon as I logout and try to login again (even with another user, and WITHOUT refreshing the page) it gives me back this error in console:
I just want to redirect the user in the dashboard if authenticated, even when the page is not refreshed cause I did notice that if I refresh the page I can login without problems.
Help me if you can. Down here some code:
Login method
methods: {
...mapActions({
attempt: "auth/attempt",
}),
submit(credentials) {
axios
.post("http://127.0.0.1:8000/api/login", credentials)
.then((res) => {
// console.log(res.data);
if (res.data.success) {
this.attempt(res.data.token)
}
if (res.data.errors) {
this.loginErrors = res.data.errors;
} else {
this.$router.push({ name: 'dashboard' })
}
})
.catch((err) => {
if (
err.name !== "NavigationDuplicated" &&
!err.message.includes(
"Avoided redundant navigation to current location"
)
) {
console.log(err);
}
});
},
},
dashboard path in the router
{
path: '/dashboard',
name: 'dashboard',
component: DashboardComponent,
beforeEnter: (to, from, next) => {
if (!store.getters['auth/authenticated']) {
return next({
name: 'home'
})
}
next()
}
},
attempt action in vuex store
async attempt({ commit, state }, token) {
if (token) {
commit('SET_TOKEN', token)
}
// se non c'รจ
if(!state.token) {
return
}
try {
await axios.get('http://127.0.0.1:8000/api/user')
.then(res => {
commit('SET_USER', res.data)
})
} catch (e) {
commit('SET_TOKEN', null)
commit('SET_USER', null)
}
},
others from vuex
namespaced: true,
state: {
token: null,
form: null,
},
getters: {
authenticated(state) {
return state.token && state.form
},
user(state) {
return state.form
},
},
mutations: {
SET_TOKEN(state, token) {
state.token = token
},
SET_USER(state, data) {
state.form = data
},
},
Update: the call to attempt() should be awaited, otherwise this.$router.push({ name: 'dashboard' }) (and therefore the guard function on the /dashboard route) will be called before the call to the /api/user API has completed:
submit(credentials) {
axios
.post("http://127.0.0.1:8000/api/login", credentials)
.then(async (res) => {
// console.log(res.data);
if (res.data.success) {
await this.attempt(res.data.token)
}
if (res.data.errors) {
this.loginErrors = res.data.errors;
} else {
this.$router.push({ name: 'dashboard' })
}
})
.catch((err) => {
if (
err.name !== "NavigationDuplicated" &&
!err.message.includes(
"Avoided redundant navigation to current location"
)
) {
console.log(err);
}
});
},
next is a function that should be called exactly once (not returned).
Try changing the code in the router to:
{
path: '/dashboard',
name: 'dashboard',
component: DashboardComponent,
beforeEnter: (to, from, next) => {
if (!store.getters['auth/authenticated']) {
next({ name: 'home' })
} else {
next()
}
}
},

How to get Vuex updated getters value in Vue custom middleware for permission check?

I have loaded all permissions when the sidebar is loading after login and getters are updated. I can access all permissions from the sidebar component.
Now I want to access all permissions in my middleware. Is it possible? What to do?
Please give a suggestion.
Here is my permission store:
const state = {
permissions: [],
user: [],
}
const getters = {
getPermissions: state => state.permissions,
getUserInfo: state => state.user,
}
const actions = {
userPermission({commit}, data) {
if (data != null) {
axios.get("/api/auth/user", {params: { token: data.token}})
.then(res => {
const per = res.data.data.permissions;
commit("setPermissions", per);
// console.log(res.data.data.permissions);
})
.catch(err => {
console.log(err);
});
}
},
userInfo({commit}, data) {
if (data != null) {
axios.get("/api/auth/user", {params: { token: data.token}})
.then(res => {
const info = res.data.data.user;
commit("setUserInfo", info);
// console.log(res.data.data.user);
})
.catch(err => {
console.log(err);
});
}
},
}
const mutations = {
setPermissions(state, data) {
state.permissions = data;
},
setUserInfo(state, data) {
state.user = data;
}
}
export default {
state,
getters,
actions,
mutations
}
Here is the middleware function:
import store from '../store';
export default (to, from, next) => {
if (isAuthenticated()) {
if (!hasPermissionsNeeded(to)) {
next('admin/permission-denied');
} else {
next();
}
next();
} else {
next('/admin/session/login');
}
};
function isAuthenticated() {
if (localStorage.getItem("userInfo") != null && localStorage.getItem("userInfo").length > 0) {
return true;
} else {
localStorage.removeItem("userInfo");
return false;
}
};
function hasPermissionsNeeded(to) {
var permissions = store.getters.getPermissions;
if(permissions.includes(to.meta.permissions) || to.meta.permissions == '*') {
return true;
} else {
return false;
}
};
Here is the router logic:
path: "/admin/country",
component: () => import("./views/admin/country/country"),
beforeEnter: authenticate,
meta : {
permissions: 'browse country'
}
I can't see where you're dispatching the userPermission action to load the permissions, but I assume you're only dispatching it somewhere that only gets called after the middleware has run. So it looks like the permissions might not have been loaded by the time you're running the middleware. You might want to dispatch the permission in the middleware, wait for it to finish and only then check the permissions. For example:
export default (to, from, next) => {
store.dispatch('userPermission').then(() => {
if (isAuthenticated()) {
...
})

How to use Axios in vue-router

I would like to know how to use Axios in vueRouter in beforeEach()
i did like that :
router.beforeEach((to, from, next) => {
axios.$http.get('http://localhost:3000/me', {
headers: {
'x-access-token': $cookies.get('user_session') //the token is a variable which holds the token
}
})
.then(response => {
if (response.status == "200") {
console.log('tet3');
}
}, response => {
this.$cookies.remove("user_session");
this.$router.push('/')
//window.location.href = "/";
console.log('erreur', response)
})
if (to.matched.some(record => record.meta.requiresAuth)) {
if (!$cookies.get('user_session')) {
next({
path: '/login',
params: {nextUrl: to.fullPath}
})
} else {
let user = JSON.parse(localStorage.getItem('user'))
if (to.matched.some(record => record.meta.is_admin)) {
if (user.is_admin == 1) {
next()
}
else {
next({name: 'userboard'})
}
}
else {
next()
}
}
} else if (to.matched.some(record => record.meta.guest)) {
if ($cookies.get('user_session') == null) {
next()
}
else {
next({name: 'userboard'})
}
} else {
next()
}
})
and the result is an error :
axios is not defined
I can't use 'this', beceause 'this' is VueRouter
Do you know how to do ?
Thank you
You have to import it:
import axios from 'axios'
const http = axios.create({
baseURL: 'http://localhost:3000/',
})
...
http.get('me', {
headers: {
'x-access-token': $cookies.get('user_session') //the token is a variable which holds the token
}
})
.then(response => {
if (response.status == "200") {
console.log('tet3');
}
}, response => {
this.$cookies.remove("user_session");
this.$router.push('/')
//window.location.href = "/";
console.log('erreur', response)
})
...

Vue-router beforeRouteEnter Vue-Resource request

I have used this link as a reference to make a request before entering a route:
https://router.vuejs.org/en/advanced/data-fetching.html
import Vue from 'vue'
import VueResource from 'vue-resource'
Vue.use(VueResource)
function getCities () {
return Vue.http({
method: 'GET',
url: process.env.base_url + 'cities'
})
}
export default {
data () {
return {
cities: []
}
},
beforeRouteEnter (to, from, next) {
getCities((err, cities) => {
if (err) {
next(false)
} else {
next(vm => {
vm.cities = cities.data
})
}
})
},
watch: {
$route () {
this.cities = []
getCities((err, cities) => {
if (err) {
this.error = err.toString()
} else {
this.cities = cities.data
}
})
}
}
However it doesn't seem to be working for me. I have tested this code and the request is successfully being made. However the result is not being returned. Currently, the request itself is being returned from the function, but I cannot show it in the beforeRouteEnter callback where it supposedly should assign it to vm.cities neither in the watch $route section.
Any help/opinion is appreciated.
The Vue.http method returns a promise, so the code should read:
beforeRouteEnter (to, from, next) {
getCities().then(response => {
next(vm => vm.cities = response.body)
}
}