Nuxt 3: plugin and global middleware infinite loop? - vue.js

Developing the app using Nuxt 3 I made a global middleware that should run before every page loads. The middleware should check the JWT token in cookies and ask the API about the user if JWT exists. It looks somehow like that.
export default defineNuxtRouteMiddleware(async (to, from) => {
const {getUser, authByHash} = useAuthStore();
await getUser();
})
The result - Nuxt DDOSes the API and sends requests every 1-2 seconds. Without any page refreshes and changes. What can it be?
P.S. Plugins execute repeatedly also

Related

Next.js cookies aren't coming through on router middleware

I'm attempting to create some route guarding using the new Next.Js 12 middleware feature. My authentication is based on a JWT token set on a cookie. I had previously implemented this using the API backend on Next.Js with no issues, and still when hitting the API routes the cookie will persist on the request no problem.
My issue appears when it will request a static page from the server. No cookies are attached so I can not determine if a User is authenticated and always redirect to a log in page. So for example the request to http://localhost:3000/ (Homepage) will not send any cookies to the middleware. But, http://localhost:3000/api/user will send a cookie to the middleware. Is there a setting I have missed in the documentation to allow this to happen?
Not sure if at all helpful but here is my _middleware.ts file that sits on the root of the pages.
import type { NextFetchEvent, NextRequest } from 'next/server';
import { NextResponse } from 'next/server';
const middleware = (req: NextRequest, ev: NextFetchEvent) => {
console.log(req.cookies);
console.log(req.cookies['user']);
console.log(req.nextUrl.pathname);
if (req.nextUrl.pathname === '/') {
return NextResponse.redirect('http://localhost:3000/login');
}
};
export default middleware;
Next.js > 12.2
req.cookies.get("user")
Before Next.js 12.2,
req.cookies?.user
I had a same problem. I use tag for routing and it works but im not sure that it is a good choice.

Vue-Router: Protecting private routes with authentication the right way

I have a /profile route that should only be accessible by an authenticated user. From research, the recommended approach is to use vue-router's navigation guard:
Here is the route object:
{
path: '/profile',
name: 'MyProfile',
component: () => import('#/views/Profile.vue'),
meta: { requiresAuth: true }
},
And here is the router's navigation guard:
router.beforeEach((to, from, next) => {
if (to.matched.some((record) => record.meta.requiresAuth)) {
if (isAuthenticated()) {
next()
}
else {
alert('Auth required!')
next('/login')
}
}
})
The isAuthenticated() function above sends a jwt token (stored in cookie) to the /jwt/validate endpoint which validates the token and returns a 200 OK response:
export function isAuthenticated() {
axios.post(`${baseUrl}/token/validate`, {}, { withCredentials: true })
.then(resp => resp.statusText == 'OK')
.catch(err => false)
}
With this approach, every time we visit the /profile route, we are making a POST request to the /token/validate endpoint. And this works quite well. However, is it too excessive to make this request every time?
My Solutions
I wonder if there is some way to store the data locally in memory. I thought of two possible solutions:
Option 1: Storing the data on vuex store, however I have learned that the vuex store object is accessible via the browser's console. This means that anyone can modify the access logic to gain access to protected routes.
Option 2: Store it inside a custom Vue.prototype.$user object, however this is similarly accessible via console and therefore has the same security risk as option 1.
Essentially, my question is: is there an option to access a protected route without having to validate the jwt token on the server-side every time?
You should query the server for if the token is valid once when the user initially loads the application. After that, there is usually no reason to check again. If the session expires on the server, then any other API calls you do should return a 401 response (or any other way that you choose to return an error) and your application can act on that.and your application can act on that.
If your profile route is getting data from the server to display and the server is properly validating the user for that request, then it doesn't matter if the user tries to manipulate the Vuex store or Vue state because they won't be able to load the data.
Doing authentication in the vue router is really more for convenience than for actual security.
Don't waste time trying to prevent a malicious user from exploring the Vue application - that is guaranteed to be a losing battle since all of the code is loaded into the browser.
If you really insist on some kind of protection, you can split the application using webpack chunks that are loaded dynamically and configure your web server to only serve those chunks to properly authenticated and authorize users. That said, I would expect such configuration to be difficult and error prone, and I don't recommend it.

Reevaluate Nuxt.js middleware without a route change

I'm wondering if it's possible to essentially "reevaluate" the middleware conditions without actually changing the current route.
The middleware's purpose is to prevent non-logged-in users from accessing the "dashboard".
My issue is, a user could become logged in or logged out without necessarily changing route but they wouldn't be redirected until they try and change pages.
I have a VueX action that triggers when the user's auth state changes but this (from what I can see), can't access the redirect or route variables.
// /mixins/auth.js
const reevaluateAuthStatus = (store, redirect, route) => {
console.log(route)
const redirectPolicy = route.meta.map((meta) => {
if (meta.auth && typeof meta.auth.redirectPolicy !== 'undefined') { return meta.auth.redirectPolicy[0] }
return []
})
const user = store.getters['auth/getUser']
if (redirectPolicy.includes('LOGGEDOUT')) {
if (user) {
return redirect('/dashboard')
}
} else if (redirectPolicy.includes('LOGGEDIN')) {
if (!user) {
return redirect('/login')
}
}
}
module.exports = {
reevaluateAuthStatus
}
// /middleware/auth.js
import { reevaluateAuthStatus } from '../mixins/auth'
export default function ({ store, redirect, route }) {
reevaluateAuthStatus(store, redirect, route)
}
Appreciate any help on this :)
You cannot re-evaluate a middleware AFAIK because it's mainly this (as stated in the documentation)
middlewares will be called [...] on the client-side when navigating to further routes
2 clean ways you can still achieve this IMO:
use some websockets, either with socket.io or something similar like Apollo Subscriptions, to have your UI taking into account the new changes
export your middleware logic to some kind of call, that you could trigger again by calling the $fetch hook again or any other data-related fetching hook in Nuxt
Some more ugly solutions would probably be:
making an internal setInterval and check if the actual state is still valid every 5s or so
move to the same page you are actually on with something like this.$router.go(0) as somehow explained in the Vue router documentation
Still, most of the cases I don't think that this one may be a big issue if the user is logged out, because he will just be redirected once he tries something.
As if the user becomes logged-in, I'm not even sure on which case this one can happen if he is not doing something pro-active on your SPA.
I don't know if it's relevant or not, but I solved a similar problem this way:
I have a global middleware to check the auth status. It's a function that receives Context as a parameter.
I have a plugin that injects itself into context (e.g. $middleware).
The middleware function is imported here.
In this plugin I define a method that calls this middleware passing the context (since the Plugin has Context as parameter as well): ctx.$middleware.triggerMiddleware = () => middleware(ctx);
Now the middleware triggers on every route change as intended, but I can also call this.$middleware.triggerMiddleware() everywhere I want.

Nuxtjs store undefined in middleware on refresh

I'm using nuxt js for my web app, I need to keep the store when I refresh the page, even in middleware or the page itself.
I'm using vuex-persistedstate;
import createPersistedState from "vuex-persistedstate";
export default ({ store }) => {
if (process.client) {
window.onNuxtReady(() => {
createPersistedState({})(store);
});
}
};
One of the solutions that I tried is to use cookies in the store, but my data is very huge and I need it in the local storage.
I think it's something related to SSR or server middleware, I tried a lot to figure out how to solve it, but nth is working.
It is impossible because we are not able to access localStorage when component is rendering on the server side. You should either turn off ssr to use localStorage or try to reduce the size of your data to put it in cookies.

Dispatch actions on authentication with Nuxt.js

I have an nuxt.js app that a user can log in to. Whenever the user is authenticated using the nuxt/auth module. I want the app to fetch some data using the nuxtServerInit.
Currently I have this in my nuxtServerInit:
export const actions = {
async nuxtServerInit({ dispatch }) {
if (this.$auth.loggedIn) {
await dispatch('products/getProducts')
...
}
}
}
This works well if I'm authenticated and the page is refreshed. The problem seems to be whenever I'm redirected after authentication, these actions are never called, thus not populating the store.
I have tried to use the fetch method instead, but in only works on the page with the fetch statement, not every page in my application. Also I don't want the http calls to be made with every page change.