use firebase auth with vue 3 route guard - vue.js

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

Related

Vue 2 router in beforeEach localStorage is not defined

Need to do something like middleware, need to check if the user has a token, then allow the transition
router.beforeEach((to, from, next) => {
const accessNeed = ['Dashboard',]
if (localStorage.getItem("token")){
if (!accessNeed.includes(to.name)) {
next({ name: 'Home' })
}else{
next()
}
}else{
next()
}
})
You are either using Nuxt, or just the Vue SSR package. So you have to make sure, the code gets executed only on client:
router.beforeEach((to, from, next) => {
if (!process.client) {
next()
return
}
const accessNeed = ['Dashboard']
if (window && window.localStorage.getItem("token")){
if (!accessNeed.includes(to.name)) {
next({ name: 'Home' })
} else{
next()
}
}
next()
})

Router async beforeEach triggers two routes

I'm trying to wait for an asynchronous response in my route guard. The problem is that two routes are hit.
When the page is loaded, the / route is triggered instantly, followed by my target route
router.beforeEach(async (to, from, next) => {
await new Promise(resolve => {
setTimeout(resolve, 500)
})
next()
})
or
router.beforeEach((to, from, next) => {
setTimeout(next, 500)
})
})
In my log I see two entries when visiting /foo, first / then /foo after 500ms. I'm expecting to see only the latter:
setup(props, { root }) {
const hideNav = computed(() => {
console.log(root.$route.path)
return root.$route.meta?.hideNav
})
return {
hideNav
}
}
})
I'm using vue#2.6.12 and vue-router#3.4.9
I'm not sure but this how I guard my route in a Vue component hope this helps you
beforeRouteEnter(to, from, next) {
if (store.getters.isAuthenticated) {
Promise.all([
store.dispatch(FETCH_ARTICLE, to.params.slug),
store.dispatch(FETCH_COMMENTS, to.params.slug)
]).then(() => {
next();
});
} else {
Promise.all([store.dispatch(FETCH_ARTICLE, to.params.slug)]).then(() => {
next();
});
}
},
Have you tried using beforeEnter?
This way you can specify which routes are going to execute your function
Like this code below:
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
})

Error: Redirected when going from "/pages/login" to "/dashboard" via a navigation guard

Guys i don't know why im get this error when i try to login, console error after login is made:
Error: Redirected when going from "/pages/login" to "/dashboard"
via a navigation guard.
Im trying when i hit login to redirect me to /dashboard page after the validation is made but when i hit login it does nothing no redirect, and if i hit again the login button the redirect is made on dashboard page. Any solutions? Thanks!
loginJWT ({ commit }, payload) {
return new Promise((resolve, reject) => {
jwt.login(payload.email, payload.password)
.then(response => {
console.log(response.data)
// If there's user data in response
if (response.data.userDetails) {
// Navigate User to homepage
router.push(router.currentRoute.query.to || '/') // <- Here is the redirect after login
// Set accessToken
localStorage.setItem('accessToken', response.data.accessToken)
// Update user details
commit('UPDATE_USER_INFO', response.data.userDetails, {root: true})
// Set bearer token in axios
commit('SET_BEARER', response.data.accessToken)
resolve(response)
} else {
reject({message: 'Wrong Email or Password'})
}
})
.catch(error => { reject(error) })
})
}
In router js:
{
path: '/dashboard',
name: 'dashboard',
component: () => import('./views/Dashboard.vue'),
meta: {
rule: 'editor',
authRequired: false
},
beforeEnter: (to, from, next) => {
guard(to, from, next)
}
}
And the guard code that validate the token after each route:
const guard = function (to, from, next) {
// check for valid auth token
const token = localStorage.getItem('accessToken')
axios.post('http://localhost:8081/api/users/checkAuthToken', {tokn: token})
.then(function (response) {
if (response.status === 200) {
next()
}
}).catch(function (error) {
if (error.response && error.response.status === 401) {
alert('access neautorizat')
next('/pages/login')
localStorage.removeItem('userInfo')
localStorage.removeItem('accessToken')
}
})
}
beforeEach code:
router.beforeEach((to, from, next) => {
const publicPages = [
'/pages/login',
'/pages/register',
'/pages/forgot-password',
'/pages/comingsoon',
'/pages/error-404',
'/pages/error-500',
'/pages/not-authorized',
'/pages/maintenance',
'/callback'
]
const authRequired = !publicPages.includes(to.path)
const loggedIn = localStorage.getItem('accessToken')
if (authRequired && !loggedIn) {
return next('/pages/login')
}
return next()
})
First Set accessToken and Last Navigate User to homepage

Vue-Router beforeEnter not function as expected?

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

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