Vue router keeps component alive after changing route - vue.js

I've to routes /home and /profile
/profile route is supposed to be private can be accessed by authenticated users only
I'm using firebase/auth and route guard to achieve that
that's the router code
const router = new Router({
path: '/',
name: 'home',
component: Home
},
{
path: '/profile',
name: 'profileSettings',
component: ProfileSettings,
beforeEnter: authGuard
},
{
path: '/auth',
name: 'auth',
component: Auth
})
function authGuard(from, to, next) {
firebase.auth().onAuthStateChanged(user => {
if (user) {
next()
} else {
next('/auth')
}
})
}
logout code
logout() {
firebase.auth().signOut().catch(error => {
console.error(error)
})
}
the template code to call logout function
Logout
After I go to /profile then return to /home and logout redirect happens to auth page

Related

Vue router - navigation guard is skipped when using router.push()

I have a Vue SPA with i18n and some views that require authentication via navigation guard.
When i am not authenticated and go to url via my browser:
examplepage.com/en/lockedpage
i get redirected to:
examplepage.com/en/login
which is good, however when i click a button that runs:
#click="$router.push(`/${$i18n.locale}/lockedpage`)"
i get in to the page even if i am not authenticated.
I want to get redirected to the login page if not authenticated
this is my router.js:
import Vue from 'vue';
import Router from 'vue-router';
import Home2 from './views/Home2.vue';
import Login from './views/Login.vue';
import Register from './views/Register.vue';
import ErrorLanding from './views/ErrorLanding.vue'
import Root from "./Root"
import i18n, { loadLocaleMessagesAsync } from "#/i18n"
import {
setDocumentLang
} from "#/util/i18n/document"
Vue.use(Router);
const { locale } = i18n
export const router = new Router({
mode: 'history',
base: '/',
routes: [
{
path: '/',
redirect: locale
},
{
path: "/:locale",
component: Root,
children: [
{
name: 'Home',
path: '',
component: Home2,
},
{
name: 'Home2',
path: '/',
component: Home2,
},
{
name: 'Login',
path: 'login',
component: Login,
},
{
path: 'register',
component: Register,
},
{
path: 'lockedpage',
name: 'lockedpage',
webpackChunkName: "lockedpage",
meta: {authRequired: true},
component: () => import('./views/LockedPage.vue')
},
{
path: '*',
component: ErrorLanding,
name: 'NotFound'
}
]
}
],
router.beforeEach((to, from, next) => {
if (to.params.locale === from.params.locale) {
next()
return
}
const { locale } = to.params
loadLocaleMessagesAsync(locale).then(() => {
setDocumentLang(locale)
const publicPages = [ `/`, `/${locale}`, `/${locale}/`];
const authRequired = !publicPages.includes(to.path);
const loggedIn = localStorage.getItem('user');
const redirect = to.path;
if (authRequired && loggedIn === null) {
if(to.meta.authRequired === false) {
next();
}
else
next({ name: 'Login', query: { redirect: redirect } });
} else {
next();
}
})
next()
});
Why does my navigationguard skip when using router.push() ?
This issue started after adding i18n, with localeredirect. so all routes comes after a locale for example: /en/.../
As Estus points out in the comments, the issue is that the first thing you're checking for is if the locales match, and if they do you're calling next() and sending the user to the page.
As outlined here:
Make sure that the next function is called exactly once in any given pass through the navigation guard. It can appear more than once, but only if the logical paths have no overlap, otherwise the hook will never be resolved or produce errors.
If you need to keep the locale check between the to and from pages, you could do something like:
if (to.params.locale === from.params.locale && loggedIn) {
next()
return
}
which will check if the loggedIn variable is truthy before pushing the user to the page they're trying to navigate to.
I believe just removing the if statement that checks if the locales match would also work.

Refreshing page redirects to home using vue router

I am facing a problem with my vuejs project. I am using vue router in a single page application. I can go to any page using vue router. But when I reload the page at any route, it redirects me to / of the project. Here is the code I have written for vue router in router/index.js file.
import Vue from 'vue'
import VueRouter from 'vue-router'
// import store from '../store'
import Home from '../views/Home.vue'
import Login from '#/components/auth/Login.vue'
import Register from '#/components/auth/Register.vue'
import Admin from '#/components/admin/Admin.vue'
import CreateCourse from '#/components/admin/course/CreateCourse.vue'
import Categories from '#/components/admin/Categories.vue'
Vue.use(VueRouter);
function isAuthenticated(to, from, next) {
// if (store.getters['auth/authenticated']) {
// next();
// } else {
// next('/login');
// }
next();
}
function isAdmin(to, from, next) {
// if (store.getters['auth/user'].role === 'super' || store.getters['auth/user'].role === 'admin') {
// next();
// } else {
// next('/');
// }
next();
}
function isNotAuthenticated(to, from, next) {
// if (!store.getters['auth/authenticated']) {
// next();
// } else {
// next('/');
// }
next();
}
const routes = [
{
path: '/',
name: 'Home',
component: Home,
},
{
path: '/about',
name: 'About',
beforeEnter: isAuthenticated,
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
},
{
path: '/login',
name: 'Login',
component: Login,
beforeEnter: isNotAuthenticated,
},
{
path: '/register',
name: 'Register',
component: Register,
beforeEnter: isNotAuthenticated,
},
{
path: '/admin',
name: 'Admin',
component: Admin,
beforeEnter: isAdmin,
},
];
const router = new VueRouter({
mode: 'history',
routes
});
export default router
What is the problem?
Note: The commented code is to control user access to a specific route. I am calling beforeEnter for each route to check if the user has permission or not. Is there any better solution?
I found what is wrong with my code. Every time I reload the page, I authenticate the user using vuex and if the authentication is successful, I redirect the user to home page. So, every time I refresh the page, I am authenticated and redirected to the home page. Now I have removed the redirect login after login and the problem is solved.

How to disable access to login and signup page when user is logged in?

i am confused why is this not working.
Routes
const router = new VueRouter({
mode: 'history',
routes: [
{ path: '/', component: Home, name: 'Home', meta: { requiresAuth: true }},
{ path: '/adminarea', component: Admin, name:"Admin", meta: { requiresAuth: true }},
{ path: '/login', component: Login, name: 'Login'},
{ path: '/signup', component: Register, name: 'Signup'},
]
});
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
// this route requires auth, check if logged in
// if not, redirect to login page.
if (!store.getters.getAuth) {
next({ name: 'Login' })
} else {
next();
}
} else {
next()
}
});
to.matched.some(record => record.meta.requiresAuth this line should allow only routes with meta requiresAuth but i don't know why is it allowing other routes as well.
I can manually access login and signup pages.
I cannot figure out what is wrong here.
I included a disableIfLoggedIn value in the router meta section
{
name: 'login',
path: '/login',
component: Login,
meta: {
disableIfLoggedIn: true
}
},
which will check before every route navigation
import {store} from "./store"; // import the store to check if authenticated
router.beforeEach((to, from, next) => {
// if the route is not public
if (!to.meta.public) {
// if the user authenticated
if (store.getters.isAuthenticated) { // I declared a `getter` function in the store to check if the user is authenticated.
// continue to the route
next();
} else {
// redirect to login
next({name: 'login'});
}
}
next();
});
Inside the store declare a getter function to check if authenticated or not
const getters = {
isAuthenticated: state => {
return state.isAuth; // code to check if authenticated
}
};
The meta value disableIfLoggedIn can be set to true in any of the router object. Then it won't be accessible after logged in.

How to not be able to access manually /login after you logged in - VueJS?

I'm learning VueJS from an Udemy course. In the module about authentication, the instructor didn't make the whole process, so I had to try it by my self for 2 days but I succeeded 90%.
The backend is on firebase, so after login with correct data, I get back a token that I send it to local storage.
With the code that I make it, you can't see the dashboard if you are not authenticated(even you try the route manually), but what I don't like is that you can see the login page after you are authenticated(if you type /signin).
This last part is not normal to be. So if you are authenticated, when you try manually to go to /signin, you can.
In the router.js:
const routes = new VueRouter({
mode: 'history',
routes: [
{ path: '/', component: WelcomePage },
{ path: '/signup', component: SignupPage },
{ path: '/signin', component: SigninPage },
{ path: '/dashboard', component: DashboardPage}
]
});
routes.beforeEach((to, from, next) => {
// redirect to login page if not logged in and trying to access a restricted page
const publicPages = ['/signin', '/signup'];
const authRequired = !publicPages.includes(to.path);
const loggedIn = localStorage.getItem('token');
console.log(loggedIn);
if (authRequired && !loggedIn) {
return next('/signin');
}
next();
});
And in store.js, inside login action:
if (localStorage.getItem('token')) {
router.replace("/dashboard");
}
Any idea what to do to /login and /register routes after login so to not be able to see them, even you manually try these routes?
If the user will try manually /signin or /signup, I want to be redirected to /dashboard.
In my vue application I just use the router.beforeEach method with meta data plus the state of my token which I pull from my store.
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
var token = store.getters.loggedIn;
if (!token ) {
next({
path: '/login',
})
} else {
next()
}
}else if (to.matched.some(record => record.meta.requiresVisitor)) {
if (token) {
next({
path: '/',
})
} else {
next()
}
}
})
It checks each time the route changes.
The requiresVisitor meta is something I placed in my router object
{
// this is can only be viewed if not logged in.
path: '/login',
name: 'login',
component: () => import(/* webpackChunkName: "login" */ './views/Login.vue'),
props: true,
meta: {
requiresVisitor: true,
layout: "landing",
},
},
{
// this can only be viewed if logged in.
path: '/',
name: 'dashboard',
component: () => import(/* webpackChunkName: "dashboard" */ './views/Dashboard.vue'),
props: true,
meta: {
requiresAuth: true,
layout: "default",
},
},
you can read more about route guards Here
theses methods are typically used in the entry point to the app main.js or in your router.js file.
You can add a per-route guard (https://router.vuejs.org/guide/advanced/navigation-guards.html#per-route-guard) to make the logic only run when /signin and /signup is matched, but if you want to keep it in the loop that runs over all routes you're on the right track -- you just need to invert your logic.
So what you want do is to add another if statement, checking if isLoggedIn is true, and that you're trying to access a public page, and in that case redirect the user to the /dashboard route.
if (!authRequired && loggedIn) {
next('/dashboard');
return;
}
Best example of redirection using beforeEach
const routes = [
{
path: "/",
name: "login",
component: Login,
meta:{requiresVisitor: true},
},
{
path: "/dashboard",
name: "dashboard",
component: Dashboard,
meta:{requiresVisitor: false},
}];
const router = new VueRouter({
mode: "history",
base: process.env.BASE_URL,
routes
});
export default router;
//Check the user is loged in If yes then move to next url.If not loged
in so move in else and check the meta and move into next other wise is
redirect my index url
router.beforeEach((to, from, next) => {
const isLogged = JSON.parse(localStorage.getItem('username'));
if (isLogged) next()
else{
if(to.meta.requiresVisitor) next()
else next('/')
}
})

Vue router navigation gaurd from within the component

I use vuex from centralized state management
in my vuex store.js i store the login status as a boolean value like below
export const store = new Vuex.Store({
state: {
loggedIn: false,
userName: 'Guest',
error: {
is: false,
errorMessage: ''
}
},
getters: {
g_loginStatus: state => {
return state.loggedIn;
},
g_userName: state => {
return state.userName;
},
g_error: state => {
return state.error;
}
}
)};
When the user logs in i set the loginstatus to true and remove the login button and replace it with log out button
everything works fine but the problem is when the user is logged in and if i directly enter the path to login component in the search bar i am able to navigate to login page again
I want to preent this behaviour
If the uses is logged in and searches for the path to loginpage in the searchbar he must be redirected to home page
I have tried using beforeRouteEnter in the login component
But we do not have acess to the this instance since the component is not yet loaded
So how can i check for login status from my store
my script in login.vue
script>
export default{
data(){
return{
email: '',
password: ''
};
},
methods: {
loginUser(){
this.$store.dispatch('a_logInUser', {e: this.email, p: this.password}).then(() =>{
this.$router.replace('/statuses');
});
}
},
beforeRouteEnter (to, from, next) {
next(vm => {
if(vm.$store.getters.g_loginStatus === true){
//what shall i do here
}
})
}
}
It is much better to put the navigation guards in routes not in pages/components and call the state getters on route file.
// /router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import store from '../store'
// Protected Pages
import Dashboard from '#/views/dashboard'
// Public Pages
import Dashboard from '#/views/login'
Vue.use(Router)
// If you are not logged-in you will be redirected to login page
const ifNotAuthenticated = (to, from, next) => {
if (!store.getters.loggedIn) {
next()
return
}
next('/') // home or dashboard
}
// If you are logged-in/authenticated you will be redirected to home/dashboard page
const ifAuthenticated = (to, from, next) => {
if (store.getters.loggedIn) {
next()
return
}
next('/login')
}
const router = new Router({
mode: 'history',
linkActiveClass: 'open active',
scrollBehavior: () => ({ y: 0 }),
routes: [
{
path: '/',
redirect: '/dashboard',
name: 'Home',
component: Full,
children: [
{
path: 'dashboard',
name: 'Dashboard',
component: Dashboard,
beforeEnter: ifAuthenticated
},
]
},
{
path: '/login',
name: 'Login',
component: Login,
beforeEnter: ifNotAuthenticated
},
{
path: '*',
name: 'NotFound',
component: NotFound
}
]
})
export default router
You can also use vue-router-sync package to get the value of store values
You can redirect the user to the home page or some other relevant page:
mounted () {
if(vm.$store.getters.g_loginStatus === true){
this.$router('/')
}
}
beforeRouteEnter (to, from, next) {
next(vm => {
if(vm.$store.getters.g_loginStatus === true){
next('/')
}
})
}
From the docs:
next: Function: this function must be called to resolve the hook. The action depends on the arguments provided to next:
next(): move on to the next hook in the pipeline. If no hooks are left, the navigation is confirmed.
next(false): abort the current navigation. If the browser URL was changed (either manually by the user or via back button), it will be reset to that of the from route.
next('/') or next({ path: '/' }): redirect to a different location. The current navigation will be aborted and a new one will be started.