middleware headers client navigation
Unanswered
Pteromalid wasp posted this in #help-forum
Pteromalid waspOP
middleware headers are not carried over during client-side navigations, how do i get the headers when navigating routes via Link? i'm using a client component wrapper in my root layout to get the headers via a server action but obviously it's not able to get them when navigating via the client (Link). using app router
root:
i just need to get the headers from middleware on every route change to be able to set some states / show toasts after the middleware session verification
"use client"
import React, { useState, useEffect } from 'react';
// import { signedIn } from '@/atoms/auth_atoms';
// import { useAtom } from 'jotai';
import { getSessionData } from '@/actions/auth_actions';
import { useAtom } from 'jotai';
import { telegramUserData, signedIn } from '@/atoms/auth_atoms';
export function SM({ children }) {
const [isSignedIn, setSignedIn] = useAtom(signedIn)
const [tud, setTud] = useAtom(telegramUserData)
console.log('[SM] signed in:', isSignedIn)
useEffect(() => {
async function fetchData() {
try {
const { userData, sessionStatus } = await getSessionData();
console.log('[SM] - userData', userData)
console.log('[SM] - sessionStatus', sessionStatus)
setTud(userData);
setSignedIn(sessionStatus);
} catch (error) {
console.error("Failed to fetch session data:", error);
} finally {
// setLoading(false);
}
}
console.log('[SM USEEFFECT CALL]')
fetchData();
}, []);
return (
<>
{children}
</>
)
}
root:
<SM>
{children}
</SM>
i just need to get the headers from middleware on every route change to be able to set some states / show toasts after the middleware session verification
55 Replies
the code you've sent seems irrelevant to me
is it about how you can retrieve headers in client components?
Pteromalid waspOP
middleware:
import { NextResponse } from 'next/server';
import { verifySession } from './actions/auth_actions';
export async function middleware(request) {
const cookie = request.cookies.get('wac');
console.log(`[Middleware] - Incoming request URL: ${request.url}`);
if (!cookie) {
// If no session cookie and trying to access a protected route:
console.log('[Middleware] - No session cookie found');
return NextResponse.redirect(new URL('/', request.url));
}
console.log(`[Middleware] - Found cookie: ${cookie}`);
const { data: verifySessionData, error: verifySessionError } = await verifySession(cookie);
if (verifySessionError) {
console.log(`[Middleware] - Session invalid or expired: ${verifySessionError}`);
// return NextResponse.redirect(new URL('/?sessionExpired=true', request.url));
// return NextResponse.redirect(new URL('/', request.url));
const response = NextResponse.redirect(new URL('/', request.url));
response.headers.set("x-session-status", "expired");
console.log('[Middleware] EXPIRED RESPONSE', response)
return response;
}
console.log('[Middleware] - Session valid', verifySessionData);
// return NextResponse.next();
const response = NextResponse.next();
response.headers.set("x-user-data", JSON.stringify(verifySessionData));
response.headers.set("x-session-status", "valid");
console.log('[Middleware] VALID RESPONSE', response)
return response;
}
// const PROTECTED_PATHS = ['/screener/:path*'];
export const config = {
matcher: ['/screener/:path*'],
};
is the headers there in Page.js?
@Alfonsus Ardani is it about how you can retrieve headers in client components?
Pteromalid waspOP
yes via client side navigation. if i refresh then i can receive the headers from the server action call in SM but not when navigating via a button / Link
ex going from index -> protected route via Link, I receive no headers in SM. if i'm on the protected page and refresh, then i can get them
when navigating, it will call server components. you can get your headers there and pass them into your client components
Pteromalid waspOP
can u explain more or give an example
when you navigate via button or <Link prefetch={false} /> to lets say /dashboard
it will call /dashboard/page.js
in that page.js, you can
it will call /dashboard/page.js
in that page.js, you can
const headerStore = await headers()
and get the header and pass it to your client component like<SM session={headerStore.get('session')} />
Pteromalid waspOP
i see. so i would have to wrap my returned items in each page with SM individually then?
ex:
/dashboard/pages.jsx
and same for every other page.jsx i want to receive headers in?
ex:
/dashboard/pages.jsx
return (
<SM session={headerStore.get('session')}>
other components
</SM>
)
and same for every other page.jsx i want to receive headers in?
yeah
headers dont naturally get forwarded to the Response of a request
when you set a header in middleware.ts, it will only modify the Request headers. which you can access later at page.js or layout.js but it will not be returned to the client
Pteromalid waspOP
"which you can access later at page.js or layout.js"
so i can do this then instead of wrapping each individual return page.jsx correct?
root layout:
so i can do this then instead of wrapping each individual return page.jsx correct?
root layout:
const headerStore = await headers()
...
<SM session={headerStore.get('session')}>
{children}
</SM>
or would it have to be each routes layout
or either?
its not a good practice
because soft navigation allows you to not rerender the root layout
which is bad
unless ure okay with that
usually you only do that in root layout BUT you check if its authenticated at every page.js
so both of these needs to be true:
<Session>{children}</Session> at root layout
AND
const auth = await getSession()
if(!auth) redirect('/login')
at every protected page
<Session>{children}</Session> at root layout
AND
const auth = await getSession()
if(!auth) redirect('/login')
at every protected page
Pteromalid waspOP
ya i would have to change the middleware to verify on every page then i guess
but that would be fine right
your middleware is already run on every page
but im just saying you dont need to wrap a SessionManagement context at every page. but instead, check auth session in the server at every page
so block unauthed user at the server before it even got to rendering the client
@Pteromalid wasp ya i would have to change the middleware to verify on every page then i guess
Pteromalid waspOP
misspoke for this, i mean't like change the matcher because my current one is
only for the dashboard
export const config = {
matcher: ['/screener/:path*'],
};
only for the dashboard
but still u don't recommend wrapping SM in the root, where would i put it then for setting state on every page
I dont recommend only checking auth in root. If you only wrap SM in root and still check auth at every page then its okay
@Alfonsus Ardani because soft navigation allows you to not rerender the root layout
Pteromalid waspOP
can u clarify this as well
user access
loads /layout.tsx
loads /dashboard/layout.tsx
loads /dashboard/page.tsx
user navigates to
loads /dashboard/edit/layout.tsx
loads /dashboard/edit/page.tsx
/dashboard
loads /layout.tsx
loads /dashboard/layout.tsx
loads /dashboard/page.tsx
user navigates to
/dashboard/edit
loads /dashboard/edit/layout.tsx
loads /dashboard/edit/page.tsx
the root layout doesn't get rerendered or re-run
which is good
but you still need to do server auth check at every page
Pteromalid waspOP
ur saying that if i have SM in root that it will not send new session headers to SM if i navigate to other routes?
it will not
Pteromalid waspOP
so instead put SM in each routes layout then instead of the root layout?
thats one of the possible solution yeah
Pteromalid waspOP
the other being in page.jsx? or is there another
the other one is where you check auth at every page.jsx
because that is the recommended approach
must haves:
1. check auth at every page.js
2. check auth at every route.ts
3. check auth at every server actions
nice to have:
1. have client component wrap session at root layout for better client component control
2. naively check auth at middleware for front-line defense
1. check auth at every page.js
2. check auth at every route.ts
3. check auth at every server actions
nice to have:
1. have client component wrap session at root layout for better client component control
2. naively check auth at middleware for front-line defense
Pteromalid waspOP
forgot to reply the other day but thank you! appreciate the time u took to help me out
one thing i changed was i'm now using url params instead of the x-session-status header bc the headers don't persist for redirects, ex from a protected page -> index. was just getting null when trying to access them
one thing i changed was i'm now using url params instead of the x-session-status header bc the headers don't persist for redirects, ex from a protected page -> index. was just getting null when trying to access them
@Pteromalid wasp forgot to reply the other day but thank you! appreciate the time u took to help me out
one thing i changed was i'm now using url params instead of the x-session-status header bc the headers don't persist for redirects, ex from a protected page -> index. was just getting null when trying to access them
url params is not a good way to store user sessions... try using cookies. it will persist in between redirects as its supposed to
Pteromalid waspOP
i'll look into cookies but the url param is just for letting me know the session expired on redirect so i can show a toast, ex /?sessionExpired=true
@Pteromalid wasp i'll look into cookies but the url param is just for letting me know the session expired on redirect so i can show a toast, ex /?sessionExpired=true
shouldnt that be handled in the client side and not server redirect stuff?
client detect that the session is expired -> await show toast -> redirect to ./login
Pteromalid waspOP
i guess i could but that's what i was using the middleware for. verify session token, if not expired -> continue, else if on protected page -> redirect to index with sessionExpired url param otherwise continue with sessionExpired url param
okay, i see
might as well
/?sessionExpired=true&next=/protectedPage
so that user gets rediercted to the same page after logging in :DPteromalid waspOP
wait wym