From 98fcb717349c484136d9845821dbc05933d947b2 Mon Sep 17 00:00:00 2001 From: DIYgod Date: Tue, 12 May 2026 16:12:34 +0800 Subject: [PATCH] fix(desktop): persist auth cookies with max age --- .../layer/main/src/lib/auth-cookies.test.ts | 53 ++++++++++++++++++- .../layer/main/src/lib/auth-cookies.ts | 15 +++++- 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/apps/desktop/layer/main/src/lib/auth-cookies.test.ts b/apps/desktop/layer/main/src/lib/auth-cookies.test.ts index a492182e6..f4c0cb83d 100644 --- a/apps/desktop/layer/main/src/lib/auth-cookies.test.ts +++ b/apps/desktop/layer/main/src/lib/auth-cookies.test.ts @@ -1,5 +1,5 @@ import type { Session } from "electron" -import { describe, expect, it, vi } from "vitest" +import { beforeEach, describe, expect, it, vi } from "vitest" import { buildManagedAuthCookieHeader, @@ -10,6 +10,10 @@ import { } from "./auth-cookies" describe("auth cookies", () => { + beforeEach(() => { + vi.useRealTimers() + }) + it("builds a cookie header from managed auth cookies only", () => { const header = buildManagedAuthCookieHeader([ { name: "__Secure-better-auth.session_token", value: "session-token" }, @@ -104,6 +108,53 @@ describe("auth cookies", () => { expect(remove).not.toHaveBeenCalled() }) + it("persists session token cookies across app restarts when the server sends Max-Age", async () => { + vi.useFakeTimers() + vi.setSystemTime(new Date("2026-05-12T00:00:00.000Z")) + + const set = vi.fn().mockImplementation(async () => {}) + const remove = vi.fn().mockImplementation(async () => {}) + const get = vi.fn().mockResolvedValue([]) + + await persistManagedAuthCookiesFromSetCookieHeader({ + apiURL: "https://api.folo.is", + session: { + cookies: { get, set, remove }, + } as unknown as Session, + setCookieHeader: + "__Secure-better-auth.session_token=session-token; Max-Age=2592000; Path=/; HttpOnly; Secure; SameSite=None", + }) + + expect(set).toHaveBeenCalledWith( + expect.objectContaining({ + name: "__Secure-better-auth.session_token", + value: "session-token", + expirationDate: 1_781_136_000, + }), + ) + }) + + it("keeps rememberMe=false session token cookies session-scoped", async () => { + const set = vi.fn().mockImplementation(async () => {}) + const remove = vi.fn().mockImplementation(async () => {}) + const get = vi.fn().mockResolvedValue([]) + + await persistManagedAuthCookiesFromSetCookieHeader({ + apiURL: "https://api.folo.is", + session: { + cookies: { get, set, remove }, + } as unknown as Session, + setCookieHeader: + "__Secure-better-auth.session_token=session-token; Path=/; HttpOnly; Secure; SameSite=None", + }) + + expect(set).toHaveBeenCalledWith( + expect.not.objectContaining({ + expirationDate: expect.any(Number), + }), + ) + }) + it("removes stale duplicate session token cookies while keeping the secure host-only cookie", async () => { const remove = vi.fn().mockImplementation(async () => {}) const get = vi.fn().mockResolvedValue([ diff --git a/apps/desktop/layer/main/src/lib/auth-cookies.ts b/apps/desktop/layer/main/src/lib/auth-cookies.ts index a79e089b9..d51d7f14e 100644 --- a/apps/desktop/layer/main/src/lib/auth-cookies.ts +++ b/apps/desktop/layer/main/src/lib/auth-cookies.ts @@ -224,6 +224,18 @@ const shouldRemoveCookie = (cookie: ParsedSetCookie) => { return false } +const getCookieExpirationDate = (cookie: ParsedSetCookie) => { + if (cookie.expirationDate !== undefined) { + return cookie.expirationDate + } + + if (cookie.maxAge !== undefined && cookie.maxAge > 0) { + return Math.floor(Date.now() / 1000) + cookie.maxAge + } + + return +} + export const getManagedAuthCookieNames = () => { return [...MANAGED_AUTH_COOKIE_NAMES] } @@ -368,6 +380,7 @@ export const persistManagedAuthCookiesFromSetCookieHeader = async ({ continue } + const expirationDate = getCookieExpirationDate(cookie) const details: CookiesSetDetails = { url: apiURL, name: cookie.name, @@ -377,7 +390,7 @@ export const persistManagedAuthCookiesFromSetCookieHeader = async ({ secure: cookie.secure, ...(cookie.sameSite ? { sameSite: cookie.sameSite } : {}), ...(cookie.domain ? { domain: cookie.domain } : {}), - ...(cookie.expirationDate ? { expirationDate: cookie.expirationDate } : {}), + ...(expirationDate ? { expirationDate } : {}), } await session.cookies.set(details)