mirror of
https://github.com/better-auth/better-auth.git
synced 2026-10-19 11:24:57 -05:00
fix(client): handle throw:true in session refresh (#8610)
This commit is contained in:
@@ -725,6 +725,103 @@ describe("session-refresh", () => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it("should preserve session when $fetch returns unwrapped data (throw: true)", async () => {
|
||||
vi.useFakeTimers();
|
||||
|
||||
const initialData = {
|
||||
user: { id: "1", email: "old@test.com" },
|
||||
session: { id: "session-1" },
|
||||
};
|
||||
|
||||
const refreshedData = {
|
||||
user: { id: "1", email: "new@test.com" },
|
||||
session: { id: "session-1" },
|
||||
};
|
||||
|
||||
const sessionAtom: SessionAtom = atom({
|
||||
data: initialData,
|
||||
error: null,
|
||||
isPending: false,
|
||||
isRefetching: false,
|
||||
});
|
||||
const sessionSignal = atom(false);
|
||||
|
||||
// When throw: true, $fetch returns data directly instead of { data, error }
|
||||
const mockFetch = vi.fn(async () => refreshedData);
|
||||
|
||||
const manager = createSessionRefreshManager({
|
||||
sessionAtom,
|
||||
sessionSignal,
|
||||
$fetch: mockFetch as any,
|
||||
options: {
|
||||
sessionOptions: {
|
||||
refetchInterval: 5,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
manager.init();
|
||||
|
||||
await vi.advanceTimersByTimeAsync(5000);
|
||||
|
||||
expect(mockFetch).toHaveBeenCalledTimes(1);
|
||||
const updatedSession = sessionAtom.get();
|
||||
expect(updatedSession.data).toEqual(refreshedData);
|
||||
expect(updatedSession.data?.user?.email).toBe("new@test.com");
|
||||
|
||||
manager.cleanup();
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it("should preserve session on visibilitychange when $fetch returns unwrapped data (throw: true)", async () => {
|
||||
vi.useFakeTimers();
|
||||
|
||||
const initialData = {
|
||||
user: { id: "1", email: "old@test.com" },
|
||||
session: { id: "session-1" },
|
||||
};
|
||||
|
||||
const refreshedData = {
|
||||
user: { id: "1", email: "new@test.com" },
|
||||
session: { id: "session-1" },
|
||||
};
|
||||
|
||||
const sessionAtom: SessionAtom = atom({
|
||||
data: initialData,
|
||||
error: null,
|
||||
isPending: false,
|
||||
isRefetching: false,
|
||||
});
|
||||
const sessionSignal = atom(false);
|
||||
|
||||
// When throw: true, $fetch returns data directly
|
||||
const mockFetch = vi.fn(async () => refreshedData);
|
||||
|
||||
const manager = createSessionRefreshManager({
|
||||
sessionAtom,
|
||||
sessionSignal,
|
||||
$fetch: mockFetch as any,
|
||||
options: {
|
||||
sessionOptions: {
|
||||
refetchOnWindowFocus: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
manager.init();
|
||||
|
||||
manager.triggerRefetch({ event: "visibilitychange" });
|
||||
await vi.runAllTimersAsync();
|
||||
|
||||
expect(mockFetch).toHaveBeenCalledTimes(1);
|
||||
const updatedSession = sessionAtom.get();
|
||||
expect(updatedSession.data).toEqual(refreshedData);
|
||||
expect(updatedSession.data?.user?.email).toBe("new@test.com");
|
||||
|
||||
manager.cleanup();
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it("should broadcast session update when broadcastSessionUpdate is called with signout", () => {
|
||||
const channel = getGlobalBroadcastChannel();
|
||||
const postSpy = vi.spyOn(channel, "post");
|
||||
|
||||
@@ -9,6 +9,24 @@ import type { AuthQueryAtom } from "./query";
|
||||
|
||||
const now = () => Math.floor(Date.now() / 1000);
|
||||
|
||||
/**
|
||||
* Normalize $fetch response: `throw: true` returns data directly, otherwise `{ data, error }`.
|
||||
*/
|
||||
function normalizeSessionResponse(res: unknown): {
|
||||
data: SessionResponse | null;
|
||||
error: unknown;
|
||||
} {
|
||||
if (
|
||||
typeof res === "object" &&
|
||||
res !== null &&
|
||||
"data" in res &&
|
||||
"error" in res
|
||||
) {
|
||||
return res as { data: SessionResponse | null; error: unknown };
|
||||
}
|
||||
return { data: res as SessionResponse, error: null };
|
||||
}
|
||||
|
||||
/**
|
||||
* Rate limit: don't refetch on focus if a session request was made within this many seconds
|
||||
*/
|
||||
@@ -90,16 +108,14 @@ export function createSessionRefreshManager(opts: SessionRefreshOptions) {
|
||||
state.lastSessionRequest = now();
|
||||
$fetch<SessionResponse>("/get-session")
|
||||
.then(async (res) => {
|
||||
let data = res.data;
|
||||
let error = res.error || null;
|
||||
let { data, error } = normalizeSessionResponse(res);
|
||||
|
||||
if (data?.needsRefresh) {
|
||||
try {
|
||||
const refreshRes = await $fetch<SessionResponse>("/get-session", {
|
||||
method: "POST",
|
||||
});
|
||||
data = refreshRes.data;
|
||||
error = refreshRes.error || null;
|
||||
({ data, error } = normalizeSessionResponse(refreshRes));
|
||||
} catch {}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user