fix(two-factor): should respect remember me value when a user logins in with 2fa (#1566)

* fix(two-factor): respect remember me value when using 2fa login

* fix(two-factor): clean up temporary cookies
This commit is contained in:
Bereket Engida
2025-02-25 09:47:10 +03:00
committed by GitHub
parent b469339996
commit da1ee9e2e6
6 changed files with 47 additions and 11 deletions
@@ -14,7 +14,6 @@ describe("cookies", async () => {
{
onResponse(context) {
const setCookie = context.response.headers.get("set-cookie");
console;
expect(setCookie).toBeDefined();
expect(setCookie).toContain("Path=/");
expect(setCookie).toContain("HttpOnly");
+18 -5
View File
@@ -148,6 +148,14 @@ export async function setSessionCookie(
dontRememberMe?: boolean,
overrides?: Partial<CookieOptions>,
) {
const dontRememberMeCookie = await ctx.getSignedCookie(
ctx.context.authCookies.dontRememberToken.name,
ctx.context.secret,
);
// if dontRememberMe is not set, use the cookie value
dontRememberMe =
dontRememberMe !== undefined ? dontRememberMe : !!dontRememberMeCookie;
const options = ctx.context.authCookies.sessionToken.options;
const maxAge = dontRememberMe
? undefined
@@ -192,7 +200,10 @@ export async function setSessionCookie(
}
}
export function deleteSessionCookie(ctx: GenericEndpointContext) {
export function deleteSessionCookie(
ctx: GenericEndpointContext,
skipDontRememberMe?: boolean,
) {
ctx.setCookie(ctx.context.authCookies.sessionToken.name, "", {
...ctx.context.authCookies.sessionToken.options,
maxAge: 0,
@@ -201,10 +212,12 @@ export function deleteSessionCookie(ctx: GenericEndpointContext) {
...ctx.context.authCookies.sessionData.options,
maxAge: 0,
});
ctx.setCookie(ctx.context.authCookies.dontRememberToken.name, "", {
...ctx.context.authCookies.dontRememberToken.options,
maxAge: 0,
});
if (!skipDontRememberMe) {
ctx.setCookie(ctx.context.authCookies.dontRememberToken.name, "", {
...ctx.context.authCookies.dontRememberToken.options,
maxAge: 0,
});
}
}
export function parseCookies(cookieHeader: string) {
@@ -291,7 +291,7 @@ export const twoFactor = (options?: TwoFactorOptions) => {
/**
* remove the session cookie. It's set by the sign in credential
*/
deleteSessionCookie(ctx);
deleteSessionCookie(ctx, true);
await ctx.context.internalAdapter.deleteSession(data.session.token);
const twoFactorCookie = ctx.context.createAuthCookie(
TWO_FACTOR_COOKIE_NAME,
@@ -220,6 +220,7 @@ export const otp2fa = (options?: OTPOptions) => {
await ctx.context.internalAdapter.deleteSession(
ctx.context.session.session.token,
);
await setSessionCookie(ctx, {
session: newSession,
user: updatedUser,
@@ -113,19 +113,27 @@ describe("two factor", async () => {
const res = await client.signIn.email({
email: testUser.email,
password: testUser.password,
rememberMe: false,
fetchOptions: {
onSuccess(context) {
onResponse(context) {
const parsed = parseSetCookieHeader(
context.response.headers.get("Set-Cookie") || "",
);
expect(parsed.get("better-auth.session_token")?.value).toBe("");
expect(parsed.get("better-auth.two_factor")?.value).toBeDefined();
expect(parsed.get("better-auth.dont_remember")?.value).toBeDefined();
headers.append(
"cookie",
`better-auth.two_factor=${
parsed.get("better-auth.two_factor")?.value
}`,
);
headers.append(
"cookie",
`better-auth.dont_remember=${
parsed.get("better-auth.dont_remember")?.value
}`,
);
},
},
});
@@ -140,15 +148,18 @@ describe("two factor", async () => {
code: OTP,
fetchOptions: {
headers,
onSuccess(context) {
onResponse(context) {
const parsed = parseSetCookieHeader(
context.response.headers.get("Set-Cookie") || "",
);
expect(parsed.get("better-auth.session_token")?.value).toBeDefined();
// max age should be undefined because we are not using remember me
expect(
parsed.get("better-auth.session_token")?.["max-age"],
).not.toBeDefined();
},
},
});
console.log({ verifyRes });
expect(verifyRes.data?.token).toBeDefined();
});
@@ -40,9 +40,14 @@ export const verifyTwoFactorMiddleware = createAuthMiddleware(
message: "invalid two factor cookie",
});
}
const dontRememberMe = await ctx.getSignedCookie(
ctx.context.authCookies.dontRememberToken.name,
ctx.context.secret,
);
const session = await ctx.context.internalAdapter.createSession(
userId,
ctx.request,
!!dontRememberMe,
);
if (!session) {
throw new APIError("INTERNAL_SERVER_ERROR", {
@@ -70,13 +75,20 @@ export const verifyTwoFactorMiddleware = createAuthMiddleware(
ctx.context.secret,
`${user.id}!${session.token}`,
);
await ctx.setSignedCookie(
trustDeviceCookie.name,
`${token}!${session.token}`,
ctx.context.secret,
trustDeviceCookie.attributes,
);
// delete the dont remember me cookie
ctx.setCookie(ctx.context.authCookies.dontRememberToken.name, "", {
maxAge: 0,
});
// delete the two factor cookie
ctx.setCookie(cookieName.name, "", {
maxAge: 0,
});
}
return ctx.json({
token: session.token,