I'm trying to redirect the user with Firebase Auth and Vue Router.
The point is that when Router redirects the user to '/' nothing is displayed in the website (completely white screen).
I know that I'm doing something wrong, but I don't know exactly what is wrong.
This is how looks like my 'router.js' file:
const router = new Router({
routes: [
{
path: '*',
redirect: '/login'
},
{
path: '/',
redirect: '/login'
},
{
path: '/login',
name: 'Login',
component: Login
},
{
path: '/',
name: 'Home',
component: Home,
meta: {
authenticated: true
}
}
]
})
router.beforeEach((to, from, next) => {
let user = firebase.auth().currentUser;
let approbation = to.matched.some(record => record.meta.authenticated)
if (approbation && !user) {
next('/login')
} else if (!approbation && user && from.path !== '/') {
next('/')
}
})
export default router
It could be that neither if (approbation && !user) nor else if (!approbation && user && from.path !== '/') is true. Since you're not calling next() after you if-else block, the hook is never resolved and vue-router doesn't know where to go.
Make sure to add a call to next() after you if-else blocks.
Related
I am trying to make a Vue 3 project with Vite, that checks if user is logged in with AWS Cognito before entering to main page.
This is my router.js:
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
import store from '../store';
import auth from '../auth';
import url from 'url'
function requireAuth(to, from, next) {
if (!auth.auth.isUserSignedIn()) {
next({
path: '/check-login',
query: { redirect: to.fullPath }
});
} else {
console.log("User already logged in");
if(store.getters.token==null) {
store.commit('setToken', auth.auth.signInUserSession.getIdToken().jwtToken);
}
next();
}
}
const routes = [
{
path: '/',
name: 'home',
component: HomeView,
beforeEnter: requireAuth
},
{
path: '/check-login', beforeEnter: () => {
auth.auth.getSession();
}
},
{
path: '/login*', beforeEnter: async () => {
let currUrl = window.location.href;
const queryObject = url.parse(currUrl,true);
const query = queryObject.hash.replace("#", "").split("&");
let id_token = "";
for(let i=0; i<query.length; i++){
if(query[i].indexOf("id_token")>-1) {
id_token = query[i];
id_token = id_token.replace("id_token=", "");
break;
}
}
if(id_token){
console.log("Setting token");
store.commit('setToken', id_token);
}
await auth.auth.parseCognitoWebResponse(currUrl);
goHome();
}
},
{
path: '/logout', beforeEnter: (to, from, next) => {
auth.logout();
next();
}
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
function goHome() {
router.push("/");
}
export default router
When I run the project using npm run dev the following warning appears:
[Vue Router warn]: No match found for location with path "/check-login?redirect=/"
I've tried to load manually other routes like login but same result. It seems that it doesn't recognize my routes (except Home), maybe because there is something wrong in the definition, but I can't find what it is...
Any idea?
Just found what was wrong. I had to include a component in every route to be detected well.
What I did is create a blank view with only a route-view inside.
In a vue 3 application that uses composition api, I want to do one of 2 things based on if an authenticated user is verified or not. So if the user is not verified, send the user to the verification page. But if the user is verified, send the user to the dashboard.
To achieve this, I am using navigation guards in my router index.js like this
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: "/signin",
name: "signin",
component: () => import("../views/SignIn.vue"),
},
{
path: "/verify",
name: "verify",
component: () => import("../views/Verification.vue"),
meta: { needsAuth: true }
},
{
path: "/dashboard",
name: "dashboard",
component: () => import("../views/UserDashboard.vue"),
meta: { needsAuth: true },
beforeEnter: (to, from, next) => {
if (localStorage.getItem('verified') == "false") next('verify')
}
},
],
});
router.beforeEach(async (to, from, next) => {
if (to.meta.needsAuth && localStorage.getItem('accessToken') == null) next('signin')
else next()
})
export default router
Then in my sigin page script setup, I have the following code
import { ref } from "vue";
import { useRouter } from "vue-router";
import axios from "axios";
const router = useRouter();
const signin = ref({
email: null,
password: null,
});
const loginUser = async () => {
try {
const res = await axios.post(
"https://mydomain/api/login",
signin.value,
{
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
}
);
localStorage.setItem("accessToken", res.data.data.accessToken);
localStorage.setItem("verified", res.data.data.verified);
router.push({ name: "dashboard" });
} catch (error) {
alert(error.response.data.message);
}
};
Now when I login with an unverified user, I get redirected to the verification page as intended. But when I login with a verified user, I never get sent to the dashboard and instead the app remains on the signin view.
I can't seem to figure out what the problem is. How can I fix this routing issue?
Did you try with next() if user is verified:
beforeEnter: (to, from, next) => {
if (localStorage.getItem('verified') == "false") next('verify')
else next()
}
I am using Vue, Vuex with Quasar (Quasar might or might not be irrelevant here)
This is my first Application and so I am not sure if I am doing things correctly or not
Below is the code snippet for my routes
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
/*
* If not building with SSR mode, you can
* directly export the Router instantiation
*/
const routes = [
{
path: '/',
component: () => import('layouts/MyLayout.vue'),
beforeEnter: ifAuthenticated ,
children: [
{ path: '', component: () => import('./../container/Index.vue') }
]
}
]
const ifNotAuthenticated = (to, from, next) => {
console.log("here")
if (!store.getters.isAuthenticated) return next()
else next('/')
}
const ifAuthenticated = (to, from, next) => {
if (store.getters.isAuthenticated) return next()
else next('/login')
}
export default function (store) {
const Router = new VueRouter({
mode: 'history',
routes,
// Leave these as is and change from quasar.conf.js instead!
// quasar.conf.js -> build -> vueRouterMode
// quasar.conf.js -> build -> publicPath
mode: process.env.VUE_ROUTER_MODE,
base: process.env.VUE_ROUTER_BASE
})
return Router
}
Here notice this line of code
const ifNotAuthenticated = (to, from, next) => {
console.log("here")
if (!store.getters.isAuthenticated) return next()
else next('/')
}
With this I was expecting to do client side authentication and based on my understanding/reading about client Authentication
I thought this would be called when I did something like this
beforeEnter: ifAuthenticated ,
But unfortunately that function isn't being called (since it isn't console logging anything).
Any idea of what I might be doing wrong?
You're not calling ifNotAuthenticated anywhere in your code, so you won't see the console log message.
It's probably easier and better to use global beforeEach guard for checking authentication. Here's a very simple example code how you can do it using meta data in your routes config.
I added comments in the code to explain things.
I recommend you to read Navigation Guards in Vue Router documentation.
Also a nice article explaining things more in detail.
const routes = [
{
path: "/dashboard",
name: "Dashboard",
component: () => import("path/to/Dashboard.vue"),
// Add a metadata like this to your private routes
// Tip: If you have a lot of private routes, you can add
// meta: { public: true } to public routes and make
// all other routes private by default.
// In this case you need to change the logic in beforeEach
// below.
meta: { requiresAuth: true },
},
];
export default function(store) {
const Router = new VueRouter({
mode: "history",
routes,
// Other stuff...
});
Router.beforeEach((to, from, next) => {
// See if any of the matched routes has meta "requiresAuth"
if (to.matched.some(route => route.meta.requiresAuth)) {
// Yes this route requires authentication. See if the user is authenticated.
if (store.getters.isAuthenticated) {
// User is authenticated, we allow access.
next();
} else {
// User is not authenticated. We can redirect her to
// our login page. Or wherever we want.
next("/login");
}
} else {
next();
}
});
return Router;
}
I have session and js checks whether if it exists. It works fine, but dashboard page is seen for miliseconds, like a blink. But I need webpage to redirect before loading html code. How can I do this? I use vue js, even beforeCreate can't help.
Like #Jayem163 said in the notes I would run the authentication verification in your router on beforeRouteEnter Below is a basic example. The router code will run before any render of your component. This way you don't have duplicate code for each route.
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
// no authentication required -- OPTIONAL
const noAuthenticationRequired = (to, from, next) => {
// in my apps right here I force logout. You don't want to go to a login page while logged in. But it's optional
next()
}
// make sure user is authenticated
const requiresAuthentication = (to, from, next) => {
// YOUR CHECK AUTH CODE WOULD GO HERE //
if (success) {
return next()
}
// not successful
next('/login')
}
export default new Router({
routes: [
{
path: '/',
name: 'dashboard',
beforeEnter: requiresAuthentication, // this route will require authentication
component: () => import('./views/Dashboard.vue')
},
{
path: '/login',
name: 'login',
beforeEnter: noAuthenticationRequired,
component: () => import('./views/Login.vue')
},
{
path: '/register',
name: 'register',
beforeEnter: noAuthenticationRequired,
component: () => import('./views/Register.vue')
},
{
path: '/password/forgot',
name: 'forgotPassword',
beforeEnter: noAuthenticationRequired,
component: () => import('./views/ForgotPassword.vue')
}
]
})
I am currently trying to only show pages, if the user is logged in. The problem I face is that requireAuth() seems to get called endless amount of times.
The code is use is:
// Routes
const routes = [
{
path: '/',
component: Dashboard,
beforeEnter: (to, from, next) => {
requireAuth(to, from, next);
},
children: [
{
path: '',
name: 'dashboard',
component: DashboardIndex
}, {
path: '*',
name: '404',
component: NotFound
}
]
}, {
path: '/login',
component: Login,
name: 'login',
},
];
function requireAuth (to, from, next) {
if (!localStorage.token) {
console.log('testing');
next({
path: '/login',
query: { redirect: to.fullPath }
})
} else {
next()
}
}
// Routing logic
let router = new VueRouter({
routes: routes,
mode: 'hash'
});
testing is output ~1000 times before I receive the error:
[vue-router] uncaught error during route navigation:
warn # app.js
app.js RangeError: Maximum call stack size exceeded
How can I make sure that /login is redirected to if !localStorage.token?
I faced this same issue as the respective error's source all boils down to next() function which is the required to navigate to the path which has to.path as value. If you'll use router.push or router.replace then possibility is to get called endless amount of times as callstack max error displays. So use simply next() and let router API do cumbersome work
I have done this type of thing, but in different manner. I handled all logic in main.js file. and routes.js file contains -
var routes = [{
path:'/login',
component: Login
},
{
path:'/',
component: dashboard
}]
Now I have controlled all type of validation in main.js file using vue-router API as taking help from this - https://router.vuejs.org/en/api/route-object.html
So now main.js would contain -
const checkToken = () => {
if(localStorage.getItem('token') == (null || undefined) ){
console.log('token is not there : ' + localStorage.getItem('token'));
return false;
}
else{
return true
}
}
//then Use `router.push('/login')` as
router.beforeEach((to, from, next) => {
if(to.path == '/') {
if(checkToken()) {
console.log('There is a token, resume. (' + to.path + ')' + 'localstorage token ' + localStorage.getItem("token"));
next();
} else {
console.log('There is no token, redirect to login. (' + to.path + ')');
router.push('/login');
}
}
So you can structure like this as control all the things in main.js and leave route.js outta everything
If you don't have a localStorage token present you are redirecting a user to /login.
Because this is also a a Vue route, your requireAuth logic will run again(because it runs for every route). Meaning you have just created a infinite loop where a user will constantly be redirected to /login even if a user is already on that page.
To stop this simply do not redirect to /login when you already are on /login.
I will leave that part to you but it shouldn't be that hard if you understand what is going on.