Add logout

This commit is contained in:
2026-06-20 20:18:58 +10:00
parent 3f9af56060
commit 21436ab98e
3 changed files with 51 additions and 2 deletions
+1 -1
View File
@@ -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
}
+30
View File
@@ -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);
+20 -1
View File
@@ -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<Response | null> {
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;
}