diff --git a/src/lib/client/stores/auth.svelte.ts b/src/lib/client/stores/auth.svelte.ts index a459b2d..a623949 100644 --- a/src/lib/client/stores/auth.svelte.ts +++ b/src/lib/client/stores/auth.svelte.ts @@ -187,14 +187,31 @@ async function register( } } -function logout(): void { +async function logout(): Promise { state.user = null; state.credits = null; saveCachedAuth(null, null); setActiveUserId(null); - // Clear local session cookie by setting expired - document.cookie = 'graphini_session=; Path=/; Max-Age=0'; - window.location.href = '/api/auth/logout'; + + // POST clears both graphini_session and graphini_guest_id server-side, then + // 302s to /. We don't follow that redirect via fetch (browsers won't apply + // its Set-Cookie to the document anyway when fetch follows redirects), so + // we read the response and force-navigate to / ourselves below. The + // Set-Cookie on the POST response is what actually removes the cookies. + // + // The earlier `window.location.href = '/api/auth/logout'` issued a GET, + // which routes to magnova-auth's federated signout — wrong for guests + // (they have no magnova session) and unnecessary for password users. + try { + await fetch('/api/auth/logout', { + method: 'POST', + credentials: 'include', + redirect: 'manual' + }); + } catch { + /* non-fatal — client state is already cleared and we force-reload below. */ + } + window.location.href = '/'; } async function refreshCredits(): Promise { diff --git a/src/routes/api/auth/logout/+server.ts b/src/routes/api/auth/logout/+server.ts index 2d6ef7f..c9d8e91 100644 --- a/src/routes/api/auth/logout/+server.ts +++ b/src/routes/api/auth/logout/+server.ts @@ -1,20 +1,22 @@ import { redirect } from '@sveltejs/kit'; import type { RequestHandler } from './$types'; -import { clearLocalSessionCookie, getSignoutUrl } from '$lib/server/auth'; +import { clearGuestCookieHeader, clearLocalSessionCookie, getSignoutUrl } from '$lib/server/auth'; export const GET: RequestHandler = async () => { - // Clear local session cookie, then redirect to magnova-auth signout + // Federated (magnova-auth) signout flow. Reserved for OAuth users who + // want their upstream session terminated too. Local clients should use + // POST so guests and password-login users don't get bounced through an + // external auth provider that has no session for them. throw redirect(302, getSignoutUrl()); }; export const POST: RequestHandler = async ({ url }) => { - // Local logout — clear graphini_session cookie and redirect to home + // Local logout — clears both the password-session cookie and the guest + // cookie. Either may be present (or both, if a guest was migrated into + // a real account mid-session); clearing both is idempotent. const secureCookie = url.protocol === 'https:'; - return new Response(null, { - status: 302, - headers: { - 'Set-Cookie': clearLocalSessionCookie(secureCookie), - Location: '/' - } - }); + const headers = new Headers({ Location: '/' }); + headers.append('Set-Cookie', clearLocalSessionCookie(secureCookie)); + headers.append('Set-Cookie', clearGuestCookieHeader(secureCookie)); + return new Response(null, { status: 302, headers }); };