fix(expo): prevent null cookie key when redirect URL has no cookie param (#7555)

This commit is contained in:
Taesu
2026-01-28 17:06:03 -08:00
committed by Alex Yang
parent 3100826d9b
commit d2ca08bc48
2 changed files with 44 additions and 6 deletions
+6 -6
View File
@@ -396,7 +396,7 @@ export const expoClient = (opts: ExpoClientOptions) => {
// Only process and notify if the Set-Cookie header contains better-auth cookies
// This prevents infinite refetching when other cookies (like Cloudflare's __cf_bm) are present
if (hasBetterAuthCookies(setCookie, cookiePrefix)) {
const prevCookie = await storage.getItem(cookieName);
const prevCookie = storage.getItem(cookieName);
const toSetCookie = getSetCookie(
setCookie || "",
prevCookie ?? undefined,
@@ -408,7 +408,7 @@ export const expoClient = (opts: ExpoClientOptions) => {
store?.notify("$sessionSignal");
} else {
// Still update the storage to refresh expiry times, but don't trigger refetch
await storage.setItem(cookieName, toSetCookie);
storage.setItem(cookieName, toSetCookie);
}
}
}
@@ -449,7 +449,7 @@ export const expoClient = (opts: ExpoClientOptions) => {
} catch {}
}
const storedCookieJson = await storage.getItem(cookieName);
const storedCookieJson = storage.getItem(cookieName);
const oauthStateValue = getOAuthStateValue(
storedCookieJson,
cookiePrefix,
@@ -468,9 +468,9 @@ export const expoClient = (opts: ExpoClientOptions) => {
);
if (result.type !== "success") return;
const url = new URL(result.url);
const cookie = String(url.searchParams.get("cookie"));
const cookie = url.searchParams.get("cookie");
if (!cookie) return;
const prevCookie = await storage.getItem(cookieName);
const prevCookie = storage.getItem(cookieName);
const toSetCookie = getSetCookie(cookie, prevCookie ?? undefined);
storage.setItem(cookieName, toSetCookie);
store?.notify("$sessionSignal");
@@ -519,7 +519,7 @@ export const expoClient = (opts: ExpoClientOptions) => {
}
}
if (url.includes("/sign-out")) {
await storage.setItem(cookieName, "{}");
storage.setItem(cookieName, "{}");
store?.atoms.session?.set({
...store.atoms.session.get(),
data: null,
+38
View File
@@ -427,6 +427,44 @@ describe("expo", async () => {
});
});
it("should not corrupt cookies when redirect URL has no cookie param", async () => {
await client.signIn.email({
email: testUser.email,
password: testUser.password,
});
const storedCookieBefore = storage.get("better-auth_cookie");
expect(storedCookieBefore).toBeDefined();
const parsedCookieBefore = JSON.parse(storedCookieBefore || "");
expect(parsedCookieBefore["better-auth.session_token"]).toBeDefined();
const expoWebBrowser = await import("expo-web-browser");
vi.mocked(expoWebBrowser.openAuthSessionAsync).mockResolvedValueOnce({
type: "success",
url: "better-auth:///dashboard", // No cookie param
});
await client.signIn.social({
provider: "google",
callbackURL: "/dashboard",
});
// Cookies should be preserved, not corrupted with "null" key
const storedCookieAfter = storage.get("better-auth_cookie");
expect(storedCookieAfter).toBeDefined();
const parsedCookieAfter = JSON.parse(storedCookieAfter || "");
// Should NOT have "null" as a cookie key
expect(parsedCookieAfter["null"]).toBeUndefined();
// Original session_token should still exist
expect(parsedCookieAfter["better-auth.session_token"]).toBeDefined();
expect(parsedCookieAfter["better-auth.session_token"]?.value).toBe(
parsedCookieBefore["better-auth.session_token"]?.value,
);
});
it("should NOT include oauthState param in proxy URL when using database strategy", async () => {
fn.mockClear();