N
Nuxt3mo ago
AlexDev

Middleware and navigateTo have weird behavior

Hello there, iam using Nuxt v3 and middleware for authentication. So iam trying to redirect a user from "a" url to url "b" when he is not authenticated. The url "a" gets appended with a query to url "b" for easy redirection. Unfornatley, my page (index.vue) has a await useFetch for ssr, this useFetch throws an 401 errror not authenticated from my backend as an error. The whole problem is that the "navigateTo" does not navigate before the ssr throws that error, there is the sample code from the middleware:
import { useUser } from "~/composable/auth";

export default defineNuxtRouteMiddleware(async (to) => {
const user = useUser();

if (user.value !== null) return;
if (to.path === "/authenticated") return;

const routeParts = to.path.split("/");
let isProtected = false,
forward = "";

for (let crrRoute of routeParts) {
if (crrRoute === "authenticated") {
isProtected = true;
continue;
}
if (isProtected) {
forward += `/${crrRoute}`;
}
}

return navigateTo(`/authenticated?forward=${forward}`, { replace: true });
});
import { useUser } from "~/composable/auth";

export default defineNuxtRouteMiddleware(async (to) => {
const user = useUser();

if (user.value !== null) return;
if (to.path === "/authenticated") return;

const routeParts = to.path.split("/");
let isProtected = false,
forward = "";

for (let crrRoute of routeParts) {
if (crrRoute === "authenticated") {
isProtected = true;
continue;
}
if (isProtected) {
forward += `/${crrRoute}`;
}
}

return navigateTo(`/authenticated?forward=${forward}`, { replace: true });
});
Note: The middleware is named: 02_auth.global.ts and there is 01_user.global.ts for user authentication Any help is appreicated, thanks!
5 Replies
kapa.ai
kapa.ai3mo ago
Hi @AlexDev. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- The behavior you're experiencing is a common challenge in Nuxt 3 when using SSR and authentication middleware. Specifically, when you use composables like useFetch or useAsyncData in your page component, those will execute before the middleware has a chance to redirect unauthenticated users, leading to 401 errors being thrown and the page rendering undesirably. Key points from the knowledge sources: - Middleware in Nuxt 3 runs before navigation, but if your page's data fetching (like useFetch) triggers a 401 before the middleware can redirect, you'll see the error before the redirect happens. - The recommended approach is to ensure that authentication checks and redirects happen before any protected data fetching occurs. This is typically achieved by using route middleware to check authentication and redirect as needed, so the protected page's logic never runs for unauthenticated users. - When using navigateTo in middleware, you must return its result to ensure the redirect works as expected. Also, using navigateTo instead of router.push or useRouter is important to avoid "glitchy" redirects and SSR issues Docs: navigateTo, Stack Overflow: Redirect to auth screen. - If you see the protected page rendered briefly before redirecting, it's often because the redirect is happening client-side after the page has already started rendering. This can be caused by using useRouter or not returning navigateTo in the middleware Stack Overflow: Redirect to auth screen. What you can do: - Make sure your authentication middleware is running before any data fetching occurs. In Nuxt 3, global middleware should run before page setup, but if you are still seeing the issue, double-check the order and logic of your global middleware. - If your authentication state is not immediately available (e.g., it requires an async check), you may need to ensure that the middleware can await the authentication check before allowing the page to proceed. - Avoid using useRouter or router.push in middleware; always use return navigateTo(...) for redirects Docs: navigateTo. - If you are using multiple global middleware files (like 01_user.global.ts and 02_auth.global.ts), ensure that the user authentication middleware runs first and sets the user state synchronously or awaits any async logic before the auth middleware checks it. Example from the docs:
export default defineNuxtRouteMiddleware((to, from) => {
const auth = useState('auth')
if (!auth.value.isAuthenticated) {
return navigateTo('/login')
}
})
export default defineNuxtRouteMiddleware((to, from) => {
const auth = useState('auth')
if (!auth.value.isAuthenticated) {
return navigateTo('/login')
}
})
This ensures the redirect happens before the page logic runs, preventing protected data fetching for unauthenticated users Docs: defineNuxtRouteMiddleware. Summary:
Your approach is correct in using middleware and navigateTo, but the issue likely lies in the timing/order of your authentication state being set and checked. Make sure your user state is available synchronously in the middleware, and always return navigateTo for redirects. If your user state is async, consider restructuring so the middleware can await it before proceeding. If you need more specific help, please provide details on how useUser is implemented and how your user state is initialized in 01_user.global.ts. --- References integrated above as per instructions. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3mo ago
<@513664776069447680> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Rylan
Rylan3mo ago
@AlexDev Its better to handle the initial auth state with a Promise.all in the index.vue. My general auth flow is the following: 1. I create a Pinia store to keep track/persist the auth token and also have some basic auth actions (functions) in there. 2. In my index.vue I have a Promise.all which generally takes in one of these auth actions to check if the auth token is there. If not, the action contains a navigateTo helper function to lead a user to a particular route. 3. My auth middleware then checks if a token exists and, depending on if it does, navigates a user to a secure route You can also experiment with using a lazy or sever params on your useUser fetch composable. Maybe something like this param: server: !!user.value ... Although it would matter what you are working with. Note, that I dont really do this, but it is an option. Hope this helps:)
AlexDev
AlexDevOP3mo ago
Thank you! Can you give me an example of your index.vue?
Cake
Cake3mo ago
fetch the user in a plugin

Did you find this page helpful?