diff --git a/src/App.tsx b/src/App.tsx index 4f223e4..48722d5 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -59,7 +59,7 @@ export default function App() { const handleLogout = useCallback(async () => { try { - await fetch('/api/auth/dev-logout', { method: 'POST', credentials: 'include' }); + await fetch('/api/auth/logout', { method: 'POST', credentials: 'include' }); } catch { // Ignore — cookie clear is best-effort } diff --git a/tests/auth-api.test.ts b/tests/auth-api.test.ts index 95b17b4..9535398 100644 --- a/tests/auth-api.test.ts +++ b/tests/auth-api.test.ts @@ -283,6 +283,36 @@ describe('GET /api/auth/me', () => { expect(db.firstCalls).toHaveLength(0); }); + it('POST /api/auth/logout clears the shared session cookie outside local dev', async () => { + const db = new FakeAuthD1(); + const env = makeEnv(db, { + PUBLIC_HOST: 'dev.heygo.cc', + PRIVATE_HOST: 'my.dev.heygo.cc', + APP_BASE_URL: 'https://dev.heygo.cc', + COOKIE_DOMAIN: '.heygo.cc', + }); + + const response = await fetchWorker('/api/auth/logout', env, { method: 'POST' }); + + expect(response.status).toBe(200); + expect(await response.json()).toEqual({ ok: true }); + const setCookie = response.headers.get('set-cookie'); + expect(setCookie).toContain(`${AUTH_SESSION_COOKIE_NAME}=;`); + expect(setCookie).toContain('Expires=Thu, 01 Jan 1970 00:00:00 GMT'); + expect(setCookie).toContain('Domain=.heygo.cc'); + expect(setCookie).toContain('HttpOnly'); + expect(response.headers.get('cache-control')).toBe('no-store'); + }); + + it('rejects GET /api/auth/logout with 405', async () => { + const db = new FakeAuthD1(); + const env = makeEnv(db); + + const response = await fetchWorker('/api/auth/logout', env, { method: 'GET' }); + + expect(response.status).toBe(405); + }); + it('falls through (returns null) for non-matching paths so the worker 404s', async () => { const db = new FakeAuthD1(); const env = makeEnv(db); diff --git a/worker/routes/api.auth.ts b/worker/routes/api.auth.ts index 946742e..7e95a4a 100644 --- a/worker/routes/api.auth.ts +++ b/worker/routes/api.auth.ts @@ -1,9 +1,28 @@ -import { getCurrentUser } from '../auth'; +import { AUTH_SESSION_COOKIE_NAME, getCurrentUser, getSessionCookieAttributes } from '../auth'; import type { Env } from '../env'; export async function handleAuthApi(request: Request, env: Env): Promise { const url = new URL(request.url); + if (url.pathname === '/api/auth/logout') { + if (request.method !== 'POST') { + return Response.json({ error: 'Method not allowed' }, { status: 405 }); + } + + const cookieAttrs = getSessionCookieAttributes(request.url, env.COOKIE_DOMAIN); + const expired = new Date(0).toUTCString(); + const cookieValue = `${AUTH_SESSION_COOKIE_NAME}=; Expires=${expired}; ${cookieAttrs.join('; ')}`; + return Response.json( + { ok: true }, + { + headers: { + 'Set-Cookie': cookieValue, + 'Cache-Control': 'no-store', + }, + }, + ); + } + if (url.pathname !== '/api/auth/me') { return null; }