import type { Cookies } from '@sveltejs/kit'; // PocketBase JWTs live in cookies shared by: // - the server hooks (authRefresh -> locals.user) // - the client SDK (seeded from page.data.pbToken -> authenticated famStore reads/subscribe) // httpOnly keeps tokens out of reach of browser JS/XSS; the client receives // the token server-side via the layout load (pbToken) and seeds pb.authStore. // Secure flag is set in prod so it's only sent over HTTPS. export const SESSION_COOKIE = 'pb_token'; // Shared-device multi-session: children hold ONE cookie per account // (`pb_token_`); `pb_active` names which one is the current session. // Parents stay on the single `pb_token`. export const CHILD_COOKIE_PREFIX = 'pb_token_'; export const ACTIVE_COOKIE = 'pb_active'; const MAX_AGE = 60 * 60 * 24 * 5; // 5 days — matches the PB users auth token duration function cookieOpts() { return { httpOnly: true, sameSite: 'lax' as const, path: '/', maxAge: MAX_AGE, secure: import.meta.env.PROD }; } export function setSessionCookie(cookies: Cookies, token: string) { cookies.set(SESSION_COOKIE, token, cookieOpts()); } export function childSessionCookie(userId: string) { return `${CHILD_COOKIE_PREFIX}${userId}`; } export function setChildSessionCookie(cookies: Cookies, userId: string, token: string) { cookies.set(childSessionCookie(userId), token, cookieOpts()); } export function clearChildSession(cookies: Cookies, userId: string) { cookies.delete(childSessionCookie(userId), { path: '/' }); } export function setActiveChild(cookies: Cookies, userId: string) { cookies.set(ACTIVE_COOKIE, userId, cookieOpts()); } export function clearActiveChild(cookies: Cookies) { cookies.delete(ACTIVE_COOKIE, { path: '/' }); } // Ids of every child that has a session cookie on this device. export function scanChildSessions(cookies: Cookies): string[] { return cookies .getAll() .filter((c) => c.name.startsWith(CHILD_COOKIE_PREFIX)) .map((c) => c.name.slice(CHILD_COOKIE_PREFIX.length)) .filter(Boolean); } // Remove every child session on this device (logout-all). export function clearDeviceSessions(cookies: Cookies) { for (const id of scanChildSessions(cookies)) clearChildSession(cookies, id); clearActiveChild(cookies); } export function clearSessionCookie(cookies: Cookies) { cookies.delete(SESSION_COOKIE, { path: '/' }); } // Legacy pre-PB-auth child cookie. No longer issued anywhere; these deletes // exist only to scrub it from browsers that still carry one. const LEGACY_DEVICE_COOKIE = 'device_token'; export function clearLegacyCookies(cookies: Cookies) { cookies.delete(LEGACY_DEVICE_COOKIE, { path: '/' }); } // ── Platform-admin session (/admin) ── // Holds a REAL PocketBase superuser JWT (minted by _superusers.authWithPassword // at login) — verified per-request in hooks via authRefresh, so forging the // cookie value gains nothing. Separate from the fam-user pb_token. export const PLATFORM_SESSION_COOKIE = 'platform_session'; const PLATFORM_MAX_AGE = 60 * 60 * 24; // 24h; PB token expiry is the ceiling export function setPlatformSession(cookies: Cookies, token: string) { cookies.set(PLATFORM_SESSION_COOKIE, token, { httpOnly: true, sameSite: 'lax', path: '/', maxAge: PLATFORM_MAX_AGE, secure: import.meta.env.PROD }); } export function clearPlatformSession(cookies: Cookies) { cookies.delete(PLATFORM_SESSION_COOKIE, { path: '/' }); }