fix: respect additionalFields returned config for user data when setting cookie cache (#5327)

Co-authored-by: Bereket Engida <86073083+Bekacru@users.noreply.github.com>
Co-authored-by: Bereket Engida <Bekacru@gmail.com>
This commit is contained in:
Ahmed Abdellahi Abdat
2025-10-15 22:10:46 +00:00
committed by GitHub
co-authored by Bereket Engida Bereket Engida
parent 713eaf7693
commit a048f804ff
2 changed files with 264 additions and 1 deletions
@@ -433,3 +433,263 @@ describe("getSessionCookie", async () => {
expect(result?.user).toBeDefined();
});
});
describe("Cookie Cache Field Filtering", () => {
it("should exclude user fields with returned: false from cookie cache", async () => {
const { client, testUser, cookieSetter } = await getTestInstance({
secret: "better-auth.secret",
user: {
additionalFields: {
internalNote: {
type: "string",
defaultValue: "",
returned: false,
},
},
},
session: {
cookieCache: {
enabled: true,
},
},
});
const headers = new Headers();
await client.signIn.email(
{
email: testUser.email,
password: testUser.password,
},
{
onSuccess: cookieSetter(headers),
},
);
const request = new Request("https://example.com/api/auth/session", {
headers,
});
const cache = await getCookieCache(request, {
secret: "better-auth.secret",
});
expect(cache).not.toBeNull();
expect(cache?.user?.email).toEqual(testUser.email);
expect(cache?.user?.internalNote).toBeUndefined();
});
it("should correctly filter multiple user fields based on returned config", async () => {
const { client, testUser, cookieSetter } = await getTestInstance({
secret: "better-auth.secret",
user: {
additionalFields: {
publicBio: {
type: "string",
defaultValue: "default-bio",
returned: true,
},
internalNotes: {
type: "string",
defaultValue: "internal-notes",
returned: false,
},
preferences: {
type: "string",
defaultValue: "default-prefs",
returned: true,
},
adminFlags: {
type: "string",
defaultValue: "admin-flags",
returned: false,
},
},
},
session: {
cookieCache: {
enabled: true,
},
},
});
const headers = new Headers();
await client.signIn.email(
{
email: testUser.email,
password: testUser.password,
},
{
onSuccess: cookieSetter(headers),
},
);
const request = new Request("https://example.com/api/auth/session", {
headers,
});
const cache = await getCookieCache(request, {
secret: "better-auth.secret",
});
expect(cache).not.toBeNull();
// Fields with returned: true should be included
expect(cache?.user?.publicBio).toBeDefined();
expect(cache?.user?.preferences).toBeDefined();
// Fields with returned: false should be excluded
expect(cache?.user?.internalNotes).toBeUndefined();
expect(cache?.user?.adminFlags).toBeUndefined();
});
it("should reduce cookie size when large fields are excluded", async () => {
const largeString = "x".repeat(2000);
const { client, testUser, cookieSetter } = await getTestInstance({
secret: "better-auth.secret",
user: {
additionalFields: {
largeBio: {
type: "string",
defaultValue: largeString,
returned: false,
},
smallField: {
type: "string",
defaultValue: "small-value",
returned: true,
},
},
},
session: {
cookieCache: {
enabled: true,
},
},
});
const headers = new Headers();
// Sign in with testUser
await client.signIn.email(
{
email: testUser.email,
password: testUser.password,
},
{
onSuccess: cookieSetter(headers),
},
);
const request = new Request("https://example.com/api/auth/session", {
headers,
});
const cache = await getCookieCache(request, {
secret: "better-auth.secret",
});
// Cookie cache should exist (not exceed size limit)
expect(cache).not.toBeNull();
// Large field should be excluded
expect(cache?.user?.largeBio).toBeUndefined();
// Small field should be included
expect(cache?.user?.smallField).toBeDefined();
});
it("should maintain session field filtering (regression check)", async () => {
const { client, testUser, cookieSetter } = await getTestInstance({
secret: "better-auth.secret",
session: {
additionalFields: {
internalSessionData: {
type: "string",
defaultValue: "internal-data",
returned: false,
},
publicSessionData: {
type: "string",
defaultValue: "public-data",
returned: true,
},
},
cookieCache: {
enabled: true,
},
},
});
const headers = new Headers();
// Sign in with testUser
await client.signIn.email(
{
email: testUser.email,
password: testUser.password,
},
{
onSuccess: cookieSetter(headers),
},
);
const request = new Request("https://example.com/api/auth/session", {
headers,
});
const cache = await getCookieCache(request, {
secret: "better-auth.secret",
});
expect(cache).not.toBeNull();
// Verify session field filtering still works correctly
// Session should have token
expect(cache?.session?.token).toEqual(expect.any(String));
// Session field with returned: false should be excluded
expect(cache?.session?.internalSessionData).toBeUndefined();
});
it("should include unknown user fields for backward compatibility", async () => {
const { client, testUser, cookieSetter } = await getTestInstance({
secret: "better-auth.secret",
user: {
additionalFields: {
knownField: {
type: "string",
defaultValue: "known-value",
returned: false,
},
},
},
session: {
cookieCache: {
enabled: true,
},
},
});
const headers = new Headers();
await client.signIn.email(
{
email: testUser.email,
password: testUser.password,
},
{
onSuccess: cookieSetter(headers),
},
);
const request = new Request("https://example.com/api/auth/session", {
headers,
});
const cache = await getCookieCache(request, {
secret: "better-auth.secret",
});
expect(cache).not.toBeNull();
// Known field with returned: false should be excluded
expect(cache?.user?.knownField).toBeUndefined();
// Standard fields like email, name should be included (backward compatibility)
expect(cache?.user?.email).toEqual(testUser.email);
expect(cache?.user?.name).toBeDefined();
});
});
+4 -1
View File
@@ -14,6 +14,7 @@ import type {
BetterAuthCookies,
GenericEndpointContext,
} from "@better-auth/core";
import { parseUserOutput } from "../db/schema";
export function createCookieGetter(options: BetterAuthOptions) {
const secure =
@@ -119,7 +120,9 @@ export async function setCookieCache(
{} as Record<string, any>,
);
const sessionData = { session: filteredSession, user: session.user };
// Apply field filtering to user data
const filteredUser = parseUserOutput(ctx.context.options, session.user);
const sessionData = { session: filteredSession, user: filteredUser };
const options = {
...ctx.context.authCookies.sessionData.options,