Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fix-auth-signout-cookie-cache.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@agent-native/core": patch
---

Clear Better Auth session cache cookies when signing out.
14 changes: 12 additions & 2 deletions packages/core/src/server/auth.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ describe("server/auth", () => {
errorSpy.mockRestore();
});

it("lets logout opt the current browser out of AUTH_DISABLED inside an HTTPS iframe", async () => {
it("opts the current browser out and forwards Better Auth logout cookies", async () => {
vi.stubEnv("NODE_ENV", "production");
vi.stubEnv("AUTH_DISABLED", "1");
delete process.env.ACCESS_TOKEN;
Expand All @@ -237,7 +237,14 @@ describe("server/auth", () => {
getSession: vi.fn(async () => null),
signInEmail: vi.fn(),
signUpEmail: vi.fn(),
signOut: vi.fn(),
signOut: vi.fn(async () => {
const headers = new Headers();
headers.append(
"set-cookie",
"better-auth.session_data=; Max-Age=0; Path=/",
);
return { headers };
}),
},
})),
getBetterAuthSync: vi.fn(() => undefined),
Expand All @@ -264,6 +271,9 @@ describe("server/auth", () => {
expect(setCookie).toContain("SameSite=None");
expect(setCookie).toContain("Secure");
expect(setCookie).toContain("Partitioned");
expect(setCookie).toContain(
"better-auth.session_data=; Max-Age=0; Path=/",
);
});

it("mounts generic Google OAuth routes by default when credentials are configured", async () => {
Expand Down
53 changes: 37 additions & 16 deletions packages/core/src/server/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -661,23 +661,23 @@ async function readDesktopSsoSafely(
* user. Returns undefined if no session cookie was minted (the common
* case — Better Auth's reset doesn't auto-sign-in by default).
*/
function getSetCookieHeaders(headers: Headers): string[] {
const responseHeaders = headers as Headers & {
getSetCookie?: () => string[];
};
return typeof responseHeaders.getSetCookie === "function"
? responseHeaders.getSetCookie()
: (responseHeaders.get("set-cookie") ?? "")
.split(/,(?=[^;]+=)/)
.map((value) => value.trim())
.filter(Boolean);
}

function extractSessionTokenFromSetCookies(
response: Response,
): string | undefined {
try {
// Headers may have multiple Set-Cookie entries; iterate via getSetCookie
// when available (Node 20+ / undici), else fall back to comma split.
const headers = response.headers as Headers & {
getSetCookie?: () => string[];
};
const setCookies =
typeof headers.getSetCookie === "function"
? headers.getSetCookie()
: (headers.get("set-cookie") ?? "")
.split(/,(?=[^;]+=)/)
.map((s) => s.trim())
.filter(Boolean);
for (const sc of setCookies) {
for (const sc of getSetCookieHeaders(response.headers)) {
// Better Auth's session cookie name is configurable but defaults to
// `<prefix>.session_token`. Match either the Better Auth default or
// our COOKIE_NAME (`an_session`) on the same line.
Expand All @@ -692,6 +692,15 @@ function extractSessionTokenFromSetCookies(
return undefined;
}

function forwardBetterAuthSetCookies(event: H3Event, result: unknown): void {
if (!result || typeof result !== "object") return;
const headers = (result as { headers?: Headers }).headers;
if (!headers || typeof headers.get !== "function") return;
for (const cookie of getSetCookieHeaders(headers)) {
event.res?.headers?.append("set-cookie", cookie);
}
}

// ---------------------------------------------------------------------------
// ACCESS_TOKEN resolution
// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -3444,7 +3453,11 @@ async function mountBetterAuthRoutes(
optOutOfAuthDisabledSession(event);

try {
await auth.api.signOut({ headers: event.headers });
const result = await auth.api.signOut({
headers: event.headers,
returnHeaders: true,
});
forwardBetterAuthSetCookies(event, result);
} catch {
// Ignore if no Better Auth session
}
Expand Down Expand Up @@ -3511,7 +3524,11 @@ async function mountBetterAuthRoutes(
clearFrameworkSessionCookies(event);
optOutOfAuthDisabledSession(event);
try {
await auth.api.signOut({ headers: event.headers });
const result = await auth.api.signOut({
headers: event.headers,
returnHeaders: true,
});
forwardBetterAuthSetCookies(event, result);
} catch {
// Ignore — sessions are already gone in DB.
}
Expand Down Expand Up @@ -3692,7 +3709,11 @@ function mountAuthFallbackRoutes(app: H3App): void {

try {
const auth = await getBetterAuth();
await auth.api.signOut({ headers: event.headers });
const result = await auth.api.signOut({
headers: event.headers,
returnHeaders: true,
});
forwardBetterAuthSetCookies(event, result);
} catch {
// Ignore if Better Auth is still unavailable
}
Expand Down
5 changes: 4 additions & 1 deletion packages/core/src/server/better-auth-instance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,10 @@ export interface BetterAuthInstance {
};
headers?: Headers;
}) => Promise<any>;
signOut: (opts: { headers: Headers }) => Promise<any>;
signOut: (opts: {
headers: Headers;
returnHeaders?: boolean;
}) => Promise<any>;
};
}

Expand Down
1 change: 1 addition & 0 deletions packages/mobile-app/components/AppWebView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ const SESSION_BRIDGE_SCRIPT = `
window.__agentNativeSessionBridgeRunning = true;
var postToken = function () {
fetch('/_agent-native/auth/session', {
cache: 'no-store',
credentials: 'include',
headers: { Accept: 'application/json' }
})
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
type: fixed
date: 2026-08-04
---

Signing out now takes effect immediately after verifying a new account.
2 changes: 1 addition & 1 deletion templates/clips/desktop/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1206,7 +1206,7 @@ export function App() {
try {
const res = await fetch(
`${serverUrl.replace(/\/+$/, "")}/_agent-native/auth/session`,
{ credentials: "include" },
{ credentials: "include", cache: "no-store" },
);
if (!res.ok) {
if (res.status === 401 || res.status === 403) {
Expand Down
Loading