Skip to content

Commit dece5ca

Browse files
Bekacruhimself65
andcommitted
fix: cookie size limit shouldn't throw error (#5031)
Co-authored-by: Alex Yang <himself65@outlook.com>
1 parent cfe6499 commit dece5ca

File tree

3 files changed

+172
-87
lines changed

3 files changed

+172
-87
lines changed

packages/better-auth/src/api/routes/sign-up.ts

Lines changed: 105 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -195,99 +195,119 @@ export const signUpEmail = <O extends BetterAuthOptions>() =>
195195
});
196196
}
197197

198-
const maxPasswordLength = ctx.context.password.config.maxPasswordLength;
199-
if (password.length > maxPasswordLength) {
200-
ctx.context.logger.error("Password is too long");
201-
throw new APIError("BAD_REQUEST", {
202-
message: BASE_ERROR_CODES.PASSWORD_TOO_LONG,
203-
});
204-
}
205-
const dbUser = await ctx.context.internalAdapter.findUserByEmail(email);
206-
if (dbUser?.user) {
207-
ctx.context.logger.info(`Sign-up attempt for existing email: ${email}`);
208-
throw new APIError("UNPROCESSABLE_ENTITY", {
209-
message: BASE_ERROR_CODES.USER_ALREADY_EXISTS_USE_ANOTHER_EMAIL,
210-
});
211-
}
212-
213-
const additionalData = parseUserInput(
214-
ctx.context.options,
215-
additionalFields as any,
216-
);
217-
/**
218-
* Hash the password
219-
*
220-
* This is done prior to creating the user
221-
* to ensure that any plugin that
222-
* may break the hashing should break
223-
* before the user is created.
224-
*/
225-
const hash = await ctx.context.password.hash(password);
226-
let createdUser: User;
227-
try {
228-
createdUser = await ctx.context.internalAdapter.createUser(
229-
{
230-
email: email.toLowerCase(),
231-
name,
232-
image,
233-
...additionalData,
234-
emailVerified: false,
235-
},
236-
ctx,
237-
);
238-
if (!createdUser) {
198+
const maxPasswordLength = ctx.context.password.config.maxPasswordLength;
199+
if (password.length > maxPasswordLength) {
200+
ctx.context.logger.error("Password is too long");
239201
throw new APIError("BAD_REQUEST", {
240-
message: BASE_ERROR_CODES.FAILED_TO_CREATE_USER,
202+
message: BASE_ERROR_CODES.PASSWORD_TOO_LONG,
241203
});
242204
}
243-
} catch (e) {
244-
if (isDevelopment) {
245-
ctx.context.logger.error("Failed to create user", e);
205+
const dbUser = await ctx.context.internalAdapter.findUserByEmail(email);
206+
if (dbUser?.user) {
207+
ctx.context.logger.info(
208+
`Sign-up attempt for existing email: ${email}`,
209+
);
210+
throw new APIError("UNPROCESSABLE_ENTITY", {
211+
message: BASE_ERROR_CODES.USER_ALREADY_EXISTS_USE_ANOTHER_EMAIL,
212+
});
246213
}
247-
if (e instanceof APIError) {
248-
throw e;
214+
215+
const additionalData = parseUserInput(
216+
ctx.context.options,
217+
additionalFields as any,
218+
);
219+
/**
220+
* Hash the password
221+
*
222+
* This is done prior to creating the user
223+
* to ensure that any plugin that
224+
* may break the hashing should break
225+
* before the user is created.
226+
*/
227+
const hash = await ctx.context.password.hash(password);
228+
let createdUser: User;
229+
try {
230+
createdUser = await ctx.context.internalAdapter.createUser(
231+
{
232+
email: email.toLowerCase(),
233+
name,
234+
image,
235+
...additionalData,
236+
emailVerified: false,
237+
},
238+
ctx,
239+
);
240+
if (!createdUser) {
241+
throw new APIError("BAD_REQUEST", {
242+
message: BASE_ERROR_CODES.FAILED_TO_CREATE_USER,
243+
});
244+
}
245+
} catch (e) {
246+
if (isDevelopment) {
247+
ctx.context.logger.error("Failed to create user", e);
248+
}
249+
if (e instanceof APIError) {
250+
throw e;
251+
}
252+
ctx.context.logger?.error("Failed to create user", e);
253+
throw new APIError("UNPROCESSABLE_ENTITY", {
254+
message: BASE_ERROR_CODES.FAILED_TO_CREATE_USER,
255+
details: e,
256+
});
249257
}
250-
throw new APIError("UNPROCESSABLE_ENTITY", {
251-
message: BASE_ERROR_CODES.FAILED_TO_CREATE_USER,
252-
details: e,
253-
});
254-
}
255-
if (!createdUser) {
256-
throw new APIError("UNPROCESSABLE_ENTITY", {
257-
message: BASE_ERROR_CODES.FAILED_TO_CREATE_USER,
258-
});
259-
}
260-
await ctx.context.internalAdapter.linkAccount(
261-
{
262-
userId: createdUser.id,
263-
providerId: "credential",
264-
accountId: createdUser.id,
265-
password: hash,
266-
},
267-
ctx,
268-
);
269-
if (
270-
ctx.context.options.emailVerification?.sendOnSignUp ||
271-
ctx.context.options.emailAndPassword.requireEmailVerification
272-
) {
273-
const token = await createEmailVerificationToken(
274-
ctx.context.secret,
275-
createdUser.email,
276-
undefined,
277-
ctx.context.options.emailVerification?.expiresIn,
278-
);
279-
const url = `${
280-
ctx.context.baseURL
281-
}/verify-email?token=${token}&callbackURL=${body.callbackURL || "/"}`;
282-
await ctx.context.options.emailVerification?.sendVerificationEmail?.(
258+
if (!createdUser) {
259+
throw new APIError("UNPROCESSABLE_ENTITY", {
260+
message: BASE_ERROR_CODES.FAILED_TO_CREATE_USER,
261+
});
262+
}
263+
await ctx.context.internalAdapter.linkAccount(
283264
{
284-
user: createdUser,
285-
url,
286-
token,
265+
userId: createdUser.id,
266+
providerId: "credential",
267+
accountId: createdUser.id,
268+
password: hash,
287269
},
288-
ctx.request,
270+
ctx,
289271
);
290-
}
272+
if (
273+
ctx.context.options.emailVerification?.sendOnSignUp ||
274+
ctx.context.options.emailAndPassword.requireEmailVerification
275+
) {
276+
const token = await createEmailVerificationToken(
277+
ctx.context.secret,
278+
createdUser.email,
279+
undefined,
280+
ctx.context.options.emailVerification?.expiresIn,
281+
);
282+
const url = `${
283+
ctx.context.baseURL
284+
}/verify-email?token=${token}&callbackURL=${body.callbackURL || "/"}`;
285+
286+
const args: Parameters<
287+
Required<
288+
Required<BetterAuthOptions>["emailVerification"]
289+
>["sendVerificationEmail"]
290+
> = ctx.request
291+
? [
292+
{
293+
user: createdUser,
294+
url,
295+
token,
296+
},
297+
ctx.request,
298+
]
299+
: [
300+
{
301+
user: createdUser,
302+
url,
303+
token,
304+
},
305+
];
306+
307+
await ctx.context.options.emailVerification?.sendVerificationEmail?.(
308+
...args,
309+
);
310+
}
291311

292312
if (
293313
ctx.context.options.emailAndPassword.autoSignIn === false ||

packages/better-auth/src/cookies/cookies.test.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,4 +368,68 @@ describe("getSessionCookie", async () => {
368368
});
369369
await expect(getCookieCache(request)).rejects.toThrow();
370370
});
371+
372+
it("should log error and skip setting cookie when data exceeds size limit", async () => {
373+
const loggerErrors: string[] = [];
374+
const mockLogger = {
375+
log: (level: string, message: string) => {
376+
if (level === "error") {
377+
loggerErrors.push(message);
378+
}
379+
},
380+
};
381+
382+
const { auth } = await getTestInstance({
383+
secret: "better-auth.secret",
384+
user: {
385+
additionalFields: {
386+
customField1: {
387+
type: "string",
388+
defaultValue: "",
389+
},
390+
customField2: {
391+
type: "string",
392+
defaultValue: "",
393+
},
394+
customField3: {
395+
type: "string",
396+
defaultValue: "",
397+
},
398+
},
399+
},
400+
session: {
401+
cookieCache: {
402+
enabled: true,
403+
},
404+
},
405+
logger: mockLogger,
406+
});
407+
408+
// Create a very large string that will exceed the cookie size limit when combined with session data
409+
// The limit is 4093 bytes, so we create data that will definitely exceed it
410+
const largeString = "x".repeat(2000);
411+
412+
// Sign up with large user data using the server API
413+
const result = await auth.api.signUpEmail({
414+
body: {
415+
name: "Test User",
416+
email: "large-data-test@example.com",
417+
password: "password123",
418+
customField1: largeString,
419+
customField2: largeString,
420+
customField3: largeString,
421+
},
422+
});
423+
424+
// Check that logger recorded an error about exceeding size limit
425+
const sizeError = loggerErrors.find((msg) =>
426+
msg.includes("Session data exceeds cookie size limit"),
427+
);
428+
expect(sizeError).toBeDefined();
429+
expect(sizeError).toContain("4093 bytes");
430+
431+
// The sign up should still succeed
432+
expect(result).toBeDefined();
433+
expect(result?.user).toBeDefined();
434+
});
371435
});

packages/better-auth/src/cookies/index.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,9 +145,10 @@ export async function setCookieCache(
145145
},
146146
);
147147
if (data.length > 4093) {
148-
throw new BetterAuthError(
149-
"Session data is too large to store in the cookie. Please disable session cookie caching or reduce the size of the session data",
148+
ctx.context?.logger?.error(
149+
`Session data exceeds cookie size limit (${data.length} bytes > 4093 bytes). Consider reducing session data size or disabling cookie cache. Session will not be cached in cookie.`,
150150
);
151+
return;
151152
}
152153
ctx.setCookie(ctx.context.authCookies.sessionData.name, data, options);
153154
}

0 commit comments

Comments
 (0)