Server side logging out via API route revalidatePath+deleting cookies - nothing seems to work
Unanswered
southclaws posted this in #help-forum
I'm trying to implement what I thought would be an extremely simple fundamental requirement of a webapp, but Next.js is proving difficult with non-obvious behaviour relating to cache revalidation and layouts. I think I understand why it's not working, but the path to solving it is not clear even after reading a lot of documentation on caching, layouts and revalidation.
My requirement is that this must be server side, because our cookies are HTTP Only and secure, I'm also strictly supporting non-JS logout, because that's a standard and has been for a long time. So I can't do any useEffect/document.cookie work, it must come from a header. That part is working fine (as the browser handles that for you) but the problem seems to be the root layout does not get revalidated after the /logout route handler redirects to either
For some context, my root layout performs a conditional render via parallel routes, there's an
(... character limit, rest in replies)
My requirement is that this must be server side, because our cookies are HTTP Only and secure, I'm also strictly supporting non-JS logout, because that's a standard and has been for a long time. So I can't do any useEffect/document.cookie work, it must come from a header. That part is working fine (as the browser handles that for you) but the problem seems to be the root layout does not get revalidated after the /logout route handler redirects to either
/ or /login. Headers are all set correctly (Clear-Site-Data, Set-Cookie, etc) but it seems I'm still not understanding the expected behaviour of revalidatePath and how that relates to layouts.For some context, my root layout performs a conditional render via parallel routes, there's an
@authenticated group and a @guest group, fairly standard as far as I can tell. In order for this to work, the root layout calls getServerSideSession which is a simple fetch request to the API to attempt to query the authenticated member's account, and if there's no cookie present this fails and returns undefined. The result of this call determines which parallel route is rendered, either @authenticated when present, or @guest when not present. All very cookie cutter as far as I can tell from Next.js docs, this looks like it's the standard recommended approach to building / landing pages which behave differently based on your logged in state (like Linear.app, Vercel.com, GitHub, etc) and I'm attempting to logout from the root page /.(... character limit, rest in replies)
5 Replies
Here's my logout route handler:
Now what's happening when this route is hit, after the user clicks the "Logout" anchor link, is it runs, successfully and the browser shows this. The headers are set, the cookie is cleared.
However, nothing happens on the actual browser viewport, the URL changes to
What I think is happening is, the root layout never actually revalidates, it doesn't re-render and thus it does not perform that parallel route switch from
The misunderstanding I can't get past is based on my understanding of
What am I missing here, how can I implement a simple logout route as I would with PHP, Go, Ruby, etc. that simply 1. clears headers, 2. clears the cache and 3. redirects to a page.
import { revalidatePath } from "next/cache";
import { cookies } from "next/headers";
import { NextResponse } from "next/server";
import { WEB_ADDRESS } from "@/config";
export const dynamic = "force-dynamic";
const cookieName = "storyden-session";
export async function GET() {
revalidatePath("/");
const destination = `${WEB_ADDRESS}/login`;
// Clear the session cookie explicitly.
cookies().delete(cookieName);
return NextResponse.redirect(destination, {
headers: {
"Clear-Site-Data": `"*"`,
"Cache-Control": "must-revalidate",
},
});
}Now what's happening when this route is hit, after the user clicks the "Logout" anchor link, is it runs, successfully and the browser shows this. The headers are set, the cookie is cleared.
However, nothing happens on the actual browser viewport, the URL changes to
/login as per the 307's Location header. All auth-required requests start to fail, as expected, not authorised as the cookie is gone. I can click around, visit pages, see errors for failed requests but am never actually properly redirected to /login to see the Login page - the expected flow after logging out. I've also tried redirecting to / but that doesn't work either, presumably because Next.js is trying to be smart and not bother re-rendering the page you're already on.What I think is happening is, the root layout never actually revalidates, it doesn't re-render and thus it does not perform that parallel route switch from
@authenticated back to @guest.The misunderstanding I can't get past is based on my understanding of
revalidatePath the root layout should completely revalidate and thus re-render after the /logout handler does what it does.What am I missing here, how can I implement a simple logout route as I would with PHP, Go, Ruby, etc. that simply 1. clears headers, 2. clears the cache and 3. redirects to a page.
Oh, and the aforementioned
getServerSideSession does have no-store set:export async function getSession() {
try {
return await accountGet({ // codegen fetch() call
cache: "no-store",
next: {
revalidate: 0,
},
});
} catch (e) {
return undefined;
}
}After much pain, I figured it out, here's the details: https://southcla.ws/how-to-implement-logout-nextjs-cache-revalidate
Masai Lion
Hey @southclaws
Do you know how to deal with this issue, or did you encountered any such issues related to deleting cookies for sub-domain: https://nextjs-forum.com/post/1296757832494088224
Do you know how to deal with this issue, or did you encountered any such issues related to deleting cookies for sub-domain: https://nextjs-forum.com/post/1296757832494088224
@Masai Lion Hey <@285684164613898243>
Do you know how to deal with this issue, or did you encountered any such issues related to deleting cookies for sub-domain: https://discord.com/channels/752553802359505017/1296757832494088224
cookies have no such concept as "delete" they either expire or are overwritten, and browsers have order of precedence rules for cookie domains, they'll also send both if it's valid.
I ran into this once which confused our team, we accidentally changed our cookie domain from
"deletion" (assuming you're not doing it from JS, which you generally shouldn't as auth cookies should be HTTP-Only) requires an exact match of the cookie's domain as you're not really deleting a cookie, you're overwriting it with an empty value and/or setting the expiry to right now
I ran into this once which confused our team, we accidentally changed our cookie domain from
company.com to .company.com and the logout stopped working because it was only overwriting the new name and kept the old one, but browsers send both regardless as they are both valid nowadays."deletion" (assuming you're not doing it from JS, which you generally shouldn't as auth cookies should be HTTP-Only) requires an exact match of the cookie's domain as you're not really deleting a cookie, you're overwriting it with an empty value and/or setting the expiry to right now