Vue router allways redirecting to error page - vue.js

i am trying to setup a redirect when the user is not logged in. But when i do it like in my example the URL changes but i get This page could not be found from nuxt. The code is inside an login.js inside the plugins folder. Then i included this in the nuxt config like this.
plugins: [
'~/plugins/login.js'
],
And here is the actual code for handling redirecting
export default ({ app, store }) => {
app.router.beforeEach((to, from, next) => {
const loggedIn = store.state.account.loggedInAccount
if (!loggedIn) {
if (to.path !== '/redirect') {
next({ path: '/redirect' })
} else {
next()
}
} else {
next()
}
})
}
It looks like the routes are not mounted yet.

You should try to use middleware. It is the conventional way to implement the beforeEach function as mentioned by the official docs. You can read about it from here. If have access to the route object, store object and redirect function inside the middleware, so use redirect to direct to the other routes after validation.

Related

Dynamically add a route in a Nuxt3 middleware

I have a Nuxt3 project where I'd like to add new routes based on an API call to a database. For example, let's say a user navigates to /my-product-1. A route middleware will look into the database and if it finds an entry, it will return that a product page should be rendered (instead of a category page, for example).
This is what I came up with:
export default defineNuxtPlugin(() => {
const router = useRouter()
addRouteMiddleware('routing', async (to) => {
if (to.path == '/my-awesome-product') {
router.addRoute({
component: () => import('/pages/product.vue'),
name: to.path,
path: to.path
})
console.log(router.hasRoute(to.path)) // returns TRUE
}
}, { global: true })
})
To keep it simple, I excluded the API call from this example. The solution above works, but not on initial load of the route. The route is indeed added to the Vue Router (even on the first visit), however, when I go directly to that route, it shows a 404 and only if I don't reload the page on the client does it show the correct page when navigated to it for the second time.
I guess it has something to do with the router not being updated... I found the following example in a GitHub issue, however, I can't get it to work in Nuxt3 as (as far as I'm aware) it doesn't provide the next() method.
When I tried adding router.replace(to.path) below the router.addRoute line, I ended up in an infinite redirect loop.
// from https://github.com/vuejs/vue-router/issues/3660
// You need to trigger a redirect to resolve again so it includes the newly added
route:
let hasAdded = false;
router.beforeEach((to, from, next) => {
if (!hasAdded && to.path === "/route3") {
router.addRoute(
{
path: "/route3",
name: "route3",
component: () => import("#/views/Route3.vue")
}
);
hasAdded = true;
next('/route3');
return;
}
next();
});
How could I fix this issue, please?
Edit:
Based on a suggestion, I tried using navigateTo() as a replacement for the next() method from Vue Router. This, however, also doesn't work on the first navigation to the route.
let dynamicPages: { path: string, type: string }[] = []
export default defineNuxtRouteMiddleware((to, _from) => {
const router = useRouter()
router.addRoute({
path: to.path,
name: to.path,
component: () => import ('/pages/[[dynamic]]/product.vue')
})
if (!dynamicPages.some(route => route.path === to.path)) {
dynamicPages.push({
path: to.path,
type: 'product'
})
return navigateTo(to.fullPath)
}
})
I also came up with this code (which works like I wanted), however, I don't know whether it is the best solution.
export default defineNuxtPlugin(() => {
const router = useRouter()
let routes = []
router.beforeEach(async (to, _from, next) => {
const pageType = await getPageType(to.path) // api call
if (isDynamicPage(pageType)) {
router.addRoute({
path: to.path,
name: to.path,
component: () => import(`/pages/[[dynamic]]/product.vue`),
})
if (!routes.some(route => route.path === to.path)) {
routes.push({
path: to.path,
type: pageType,
})
next(to.fullPath)
return
}
}
next()
})
})
I suggest you use dynamic routing within /page directory structure - https://nuxt.com/docs/guide/directory-structure/pages#dynamic-routes
The [slug] concept is designed exactly for your usecase. You don't need to know all possible routes in advance. You just provide a placeholder and Nuxt will take care of resolving during runtime.
If you insist on resolving method called before each route change, the Nuxt's replacement for next() method you're looking for is navigateTo
https://nuxt.com/docs/api/utils/navigate-to
And I advise you to use route middleware and put your logic into /middleware/routeGuard.global.ts. It will be auto-executed upon every route resolving event. The file will contain:
export default defineNuxtRouteMiddleware((to, from) => {
// your route-resolving logic you wanna perform
if ( /* navigation should happen */ {
return navigateTo( /* your dynamic route */ )
}
// otherwise do nothing - code will flow and given to.path route will be resolved
})
EDIT: However, this would still need content inside /pages directory or some routes created via Vue Router. Because otherwise navigateTo will fail, as there would be no route to go.
Here is an example of one possible approach:
https://stackblitz.com/edit/github-8wz4sj
Based on pageType returned from API Nuxt route guard can dynamically re-route the original URL to a specific slug page.

How to perform a SignOut action in Nuxt.js

I'm currently working on porting the following router.beforeEach action that I have for a Vue.js application to something workable within Nuxt.js.
I've had a good trawl of the middleware documentation, but I'm not quite sure what the correct pattern would be to follow.
My callback which runs before every route change in my Vue.js application is as:
// This callback runs before every route change, including on page load.
router.beforeEach(async (to, from, next) => {
// Reset All State When A User Logs Out:
if (to.redirectedFrom === '/sign-out') {
store.dispatch('auth/resetAuthState')
Vue.prototype.authAPI.cleanseLocalStorage()
}
if (to.meta.authenticationRequired) {
if (!store.getters['auth/activeUserIsAuthenticated']) {
next({ name: 'signIn' })
} else {
next()
}
} else {
next()
}
})
I have the following redirect in my Vue router to perform the redirect action:
...
{
path: '/sign-out',
name: 'signOut',
redirect: {
name: 'signIn'
},
meta: {
...{
authenticationRequired: false,
sitemap: {
ignoreRoute: true
}
}
}
},
...
So on a SignOut redirect, I cleanse local storage and do some further state management inside the Vuex store.
However, I have no idea where to start this with Nuxt.js - any ideas would be greatly appreciated.
I suggest that using "Nuxt/auth" for handling your authentication. you can read this docs here:
https://auth.nuxtjs.org/
On a Nuxt.js project I worked on, I created a sign-out.vue page and emptied the localstorage from there, then redirected to the homepage.

Redirected when going from to via navigation guard

I'm trying to protect my Vue components using the Vue router authentication guard.
Case scenario: unauthenticated user lands on home page ("/" route) and he's trying to access "/profile", but that's a private component, so he'll be redirected by the vue router to "/auth/profile", so he'll authenticate and then the Auth component will redirect the user to the "/profile" component, because he got its path in the URL.
That's my guard
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.private)) {
if (!store.getters.getUser) {
//console.log("PRIVATE - NOT authenticated");
next({ path: "/auth" + `${to.path}` });
} else {
//console.log("PRIVATE - authenticated");
next();
}
} else {
//console.log("NOT PRIVATE");
next();
}
});
Everything works as expected, but I get an error and it's annoying
Redirected when going from "/" to "/profile" via a navigation guard.
Somewhere in your code, after being redirected to "/profile", you are being redirected back to "/". And that is what the vue-router is complaining about.
So the problem resides in being redirected multiple times per action.
You'll want to make sure you only have one redirect per navigation action.
problem solved by replacing
next({ name: "Onboarding" });
with
router.push({ path: 'Onboarding' });
Reduce vue-router version to 3.0.7, or
follow code into your app.js or index.js, which one you import vue-router
example:
import Vue from 'vue';
import VueRouter from 'vue-router';
const originalPush = VueRouter.prototype.push
VueRouter.prototype.push = function push(location, onResolve, onReject) {undefined
if (onResolve || onReject) return originalPush.call(this, location, onResolve, onReject)
return originalPush.call(this, location).catch(err => err)
}
Vue.use(VueRouter);
...
#code
This could be because your other component (the one you are pointing to) is redirecting you back.

Vue Router and Laravel Middleware

I want to add middleware in vue router, using laravel auth. If auth is checked then it should be allowed to open url otherwise retun back.
You can use vue-router Navigation Guards
as per your code i will try to add an example
{
path: '/',
component: HomePage,
beforeEnter: requireAuth
}
My navigation guard will be requireAuth as
function requireAuth(to, from, next) {
if (login) { //you local login check, you can either use vuex or local storage or cookies etc
next()
} else {
next("/login")
}
}

Nuxt js <nuxt-link /> authentication

I have a route in nuxt that has to be accessible only by logged in users: /dashboard/secret.
In /dashboard page I have a link like this:
<nuxt-link to="/dashboard/secret">Link to "secret" page</nuxt-link>
When clicked, nuxt will fetch that page from
myapp.com/_nuxt/pages_dashboard_secret.js
How can I add authentication for that nuxt route to stop people from manually going to that url and reading the contents?
Yes the actual secret data will be taken from external api which will validate user token, but still it seems wrong that people can see even the html of this page
if you just want to protect a js file, it would be wrong to do it like this. But if you mean you just want to protect a route from being accessed manually by the users, you must try Nuxt Middlewares and write a middleware for authentication and user fetching.
The middleware structure can be as simple as this:
export default function ({ store, redirect }) {
// If the user is not authenticated
if (!store.state.authenticated) {
return redirect('/login')
}
}
and you can simply use it like this in your root (or secretPage) layout:
<template>
<h1>Secret page</h1>
</template>
<script>
export default {
middleware: 'authenticated'
}
</script>
You can use nuxt/auth package, that is the case for your work and can be used as a plugin and module, you can check has it for the be accessible page or not, it runs automatically and has semantic structure.
You cannot keep your secret on client side (in your JS code) everyone using your application can get it from his browser. You need to keep secret keys on server side and make some validation endpoint to provide if user is valid or not or just keep his session after login.
you can use middleware in nuxt framework. Also, route has a information about url and request. You can make a logic by using them.
https://nuxtjs.org/docs/directory-structure/middleware/
middleware/auth.js
export default async function ({store, from, route, req}) {
if (process.client) {
if (route.name === 'dashboard-room-id' && from.name === route.name)
return
else await store.dispatch('checkSession', route)
}
}
save the token in the store on nuxtServerInit or whenever you get it.
on /dashboard/secret page check in the fetch method if there is a token set.
if token is set, fetch your data otherwise redirect the use somewhere else
https://nuxtjs.org/examples/auth-routes/#redirect-user-if-not-connected
For such a guard of pages, the middleware is the sure way to do it.
Create a middleware file in the middleware directory
Add your middleware logic as described here https://nuxtjs.org/api/pages-middleware/
Then add the middleware option in your page component
as it is mentioned that the routing should be done on the server, in case you just want to handle it if I have this
store/index.js action
async nuxtServerInit({ dispatch, commit }, { req }) {
try {
if (process.server && process.static) { return }
if (!req.headers.cookie) {
console.log('return ')
return
}
const parsed = cookieparser.parse(req.headers.cookie)
const accessTokenCookie = parsed.__session
if (!accessTokenCookie) { return }
const decoded = JWTDecode(accessTokenCookie)
if (userData.exists) {
commit('setState', { name: 'user',
value: {
uid: decoded.user_id,
email: decoded.email,
...userData.data()
} })
}
} catch (e) {
console.log(e)
}
},
//Login firebase
async fireLogin({ dispatch }, { singInWith, account }) {
const resp = await this.$firebase.auth()signInWithEmailAndPassword(account.email, account.password)
const token = await resp.user.getIdToken()
Cookie.set('__session', token)
return { email: resp.user.email, uid: resp.user.uid }
}
Middleware/auth.js
export default function({ store, route, redirect }) {
const user = store.state.user
const blockedRoute = /\/admin\/*/g
const homeRoute = '/'
if (!user && route.path.match(blockedRoute)) {
redirect('/')
}
/*if (user && route.path === homeRoute) {
redirect('/admin')
}*/
}
nuxt.config
router: {
middleware: [
'authenticated'
]
},
you can set the middleware for current page
middle ware
export default context => {
//set Condition and logic
};
route page :
middleware: 'name of middle ware'
i can suggest three solutions:
1.Get pathname in your js codes and then check the url that client using to access your page , for example if pathname is
/dashboard/secret and user is logged in then show the page
for checking pathname u can use these cods:
$nuxt.$route.path
//or good old pure js ;)
window.location.pathname
2.check if user truly logged in (backend & frontend)
for that u can use nuxt-auth and sync it to your backend as well.
for example if you using laravel , u can use laravel passport ,
in that case when the request sended to the backend route, you can check if user is logged in to the backend as well.
Ps:This way is more secure and of course in every backend language this process can be different, but surely all of them will have the same capability.
3.using .htaccess :
Do not allow the user to view the file directly from the server path
Read more