I have a vue app which uses the vue router. I implemented an authentication system for the login routes. How do I get it to redirect automatically to the route the user initially wanted to visit after login was successfull?
This is my code
import Vue from "vue";
import VueRouter, { RouteConfig } from "vue-router";
import Home from "../views/Home.vue";
import Collections from "../views/Collections.vue";
import Login from "../views/Login.vue";
import store from "../store/index";
Vue.use(VueRouter);
const routes: Array<RouteConfig> = [
{
path: "/login",
name: "Login",
component: Login,
},
{
path: "/",
name: "Home",
component: Home,
},
{
path: "/collections",
name: "Collections",
component: Collections,
//meta: { requiresAuth: true }
},
];
const router = new VueRouter({
mode: "history",
base: process.env.BASE_URL,
routes,
});
router.beforeEach((to, from, next) => {
if (!store.getters.isAuthenticated && to.name !== "Login") {
next("/login");
} else {
next();
}
});
export default router;
Instead of just calling next('/login'), you can additionally pass the route that users want to visit as a query parameter, like this:
next({
path: '/login',
query: {
redirect: to.fullPath,
}
})
Then, in the login page, after the successful authentication, you can call
this.$router.replace(this.$route.query.redirect || '/');
It will redirect users to the page they initially want to visit, or to home if there is no information about redirection.
Use cookie storage
this.cookieUrl = this.$route.query.redirecturl
if (this.cookieUrl) {
cookieStorage.setItem('redirecturl', this.cookieUrl, false, 2000000)
}
and after login check cookie
if (cookieStorage.getItem('redirecturl')) {
this.$router.push({
path: cookieStorage.getItem('redirecturl')
})
cookieStorage.removeItem('redirecturl')
The plain and simple approach which could work in that case is simply redirecting back to previous route after successful login.
$router.go(-1)
Your best option is to initially redirect the person to the login page with a parameter that specifies the original page. Something like:
if(!loggedIn) {
// If not authenticated, add a path where to redirect after login.
this.$router.push({ name: 'login', query: { source_path: '/current_route' }});
}
(you can optionally use this.$route.query.page in place of an explicit /current_route string).
Then in the login page have a method that uses the parameter if set, or falls back to the homepage:
this.$router.push(this.$route.query.source_path || '/')
Related
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.
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.
Im trying to figure out why Vue.js routing, after I refresh my admin page re-directs back to the home component, only showing a blank page, and after a second refresh shows the home component again. I am still logged in as I can still go directly with the url to my admin-page. Meaning the session is still active. Is there a way to force the page to stay on the admin home page when I press F5? I tried things like history mode etc, but cant figure it out.
This is my router.js layout in the root and alos my main router file
import Vue from 'vue'
import Router from 'vue-router'
import firebase from "firebase/app";
import Home from './views/user/Home.vue'
import adminRoute from "./components/routes/admin-routes";
Vue.use(Router);
const router = new Router({
routes: [
{
path: '*',
redirect: '/',
},
{
path: '/',
name: 'home',
component: Home,
meta: {
requiresAuth: false,
}
},
...adminRoute
],
});
router.beforeEach((to, from, next) => {
if (to.matched.some(r => r.meta.requiresAuth === false)) {
next()
} else if (!firebase.auth().currentUser) {
next(false)
} else {
next()
}
});
export default router
and I have my admin-routes.js
import AdminHome from "../../views/admin/AdminHome";
import Users from "../../views/admin/Users";
const adminRoute = [
{
path: '/admin-home',
name: 'AdminHome',
component: AdminHome,
meta: {
requiresAuth: true
},
},
{
path: '/users',
name: 'Users',
component: Users,
meta: {
requiresAuth: true,
}
}
];
export default adminRoute;
I do want to mention that my main page is under views/user/Home.vue and my AdminHome page is views/admin/AdminHome.vue
This problem is return to the server not the built app
you have to manage your 404 not found root
Here you can find the solution
and if you work With Netlify you just create a file in the root of the project ( same place as package.json) name netlify.toml containing
[[redirects]]
from = "/*"
to = "/index.html"
status = 200
in vue4 they did away with the 'next' argument(see here), this is working for me to send people back to the login page, where I'm using firebaseui, I have a simple boolean flag in my vuex store named "isAuthenticated" that I flip on when a user signs on.
NOTE: the following is a boot file for quasar v2 but the logic will
work where ever you have access to the store
import { computed } from 'vue'
export default ({ app, router, store }) => {
console.log('nav.js:')
const isAuthenticated = computed(() => store.state.auth.isAuthenticated)
console.log('isAuthenticated',isAuthenticated.value)
router.beforeEach((to, from) => {
console.log('nav.js:beforeEach:to',to)
console.log('nav.js:beforeEach:from',from)
if (to.matched.some(record => record.meta.requiresAuth)) {
if (!isAuthenticated.value) {
console.log('nav.js:beforeEach:to.redirect')
return '/login'
}
}
})
}
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('/')
}
})
I have issue that i still cant fix it after i try some solution thought google search.
I am using router.beforeEach to check token exist.
If I no login and input localhost/overview in url bar directly,
it need to redirect login page and url redirect to localhost/login but fail and show the error message below,
Maximum call stack size exceeded
How can i modify the router.beforeEach method ?
In router.js
import Vue from 'vue'
import store from '#/store/index'
import Router from 'vue-router'
import Overview from '#/components/Overview'
import Login from '#/components/Login'
Vue.use(Router)
const router = new Router({
mode: 'history',
routes: [
{
path: '/',
name: 'Login',
component: Login,
meta: {
requiresAuth: false
}
},
{
path: '/overview',
name: 'Overview',
component: Overview,
meta: {
requiresAuth: true
}
},
{
path: '*',
redirect: '/login',
meta: {
requiresAuth: false
}
}
]
}
router.beforeEach((to, from, next) => {
let token = store.state.token
if (token) {
if (to.query.redirect) {
next({path: to.query.redirect})
} else {
if (['/login'].includes(to.path)) {
next({path: '/overview'})
} else {
next()
}
}
} else {
let requiresAuth = to.matched.some(record => record.meta.requiresAuth)
if (requiresAuth) {
next({path: '/login', query: { redirect: to.fullPath }})
} else {
next()
}
}
})
export default router
Be sure that your logging path does not require auth itself/that your mechanism to detect if a page needs auth is correct.
Keep in mind that your navigation guard function will be called each time a route is reached, including when you are redirecting the user programatically in your navigation guard itself. So you should take that in account to create a function that will not loop (i.e trigger a redirect that trigger a redirect...).