fix: get-session should have no-cache cache control headers (#10222)

This commit is contained in:
Maxwell
2026-07-07 00:08:47 +00:00
committed by GitHub
parent d7c41baa1f
commit 46d2bf02c9
3 changed files with 88 additions and 1 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"better-auth": patch
---
fix: add no-store cache-control headers to get-session route
@@ -2433,3 +2433,76 @@ describe("forced strict session validation", async () => {
});
});
});
describe("get-session cache headers", async () => {
/**
* @see https://github.com/better-auth/better-auth/issues/10217
*/
it("sets Cache-Control: no-store on authenticated GET /get-session responses", async () => {
const { auth, client, testUser, cookieSetter } = await getTestInstance();
const headers = new Headers();
await client.signIn.email(
{ email: testUser.email, password: testUser.password },
{ onSuccess: cookieSetter(headers) },
);
const res = await auth.handler(
new Request("http://localhost:3000/api/auth/get-session", {
headers: { cookie: headers.get("cookie") || "" },
}),
);
expect(res.status).toBe(200);
const body = (await res.json()) as { user?: { id: string } } | null;
expect(body?.user?.id).toBeTruthy();
expect(res.headers.get("cache-control")).toContain("no-store");
});
/**
* @see https://github.com/better-auth/better-auth/issues/10217
*/
it("sets Cache-Control: no-store on unauthenticated GET /get-session responses", async () => {
const { auth } = await getTestInstance();
const res = await auth.handler(
new Request("http://localhost:3000/api/auth/get-session"),
);
expect(res.status).toBe(200);
expect(await res.json()).toBeNull();
expect(res.headers.get("cache-control")).toContain("no-store");
});
/**
* @see https://github.com/better-auth/better-auth/pull/10222
*/
it("does not set Cache-Control: no-store on session-gated endpoints", async () => {
const sessionGatedPlugin = {
id: "session-gated-cache-test",
endpoints: {
sessionGatedCheck: createAuthEndpoint(
"/session-gated-cache-check",
{
method: "GET",
use: [freshSessionMiddleware],
},
async () => ({ status: true }),
),
},
};
const { auth, signInWithTestUser } = await getTestInstance({
plugins: [sessionGatedPlugin],
});
const { headers } = await signInWithTestUser();
const res = await auth.handler(
new Request("http://localhost:3000/api/auth/session-gated-cache-check", {
headers: { cookie: headers.get("cookie") || "" },
}),
);
expect(res.status).toBe(200);
expect(res.headers.get("cache-control")).toBeNull();
});
});
+10 -1
View File
@@ -72,6 +72,9 @@ export const getSession = <Option extends BetterAuthOptions>() =>
session: Session<Option["session"], Option["plugins"]>;
user: User<Option["user"], Option["plugins"]>;
} | null> => {
ctx.setHeader("cache-control", "no-store");
ctx.setHeader("pragma", "no-cache");
const deferSessionRefresh =
ctx.context.options.session?.deferSessionRefresh;
const isPostRequest = ctx.method === "POST";
@@ -584,9 +587,15 @@ export const getSessionFromCtx = async <
}
if (session.headers) {
session.headers.forEach((value, key) => {
const lowerKey = key.toLowerCase();
// /get-session response cache headers must not leak onto endpoints
// that resolve the session via getSessionFromCtx.
if (lowerKey === "cache-control" || lowerKey === "pragma") {
return;
}
if (!ctx.context.responseHeaders) {
ctx.context.responseHeaders = new Headers({ [key]: value });
} else if (key.toLowerCase() === "set-cookie") {
} else if (lowerKey === "set-cookie") {
ctx.context.responseHeaders.append(key, value);
} else {
ctx.context.responseHeaders.set(key, value);