/** * Cookie configuration for authentication * All cookies use 'rev.' prefix, HttpOnly, Secure, SameSite=Lax */ export const COOKIE_NAMES = { SESSION_TOKEN: "rev.session_token", DEVICE_FINGERPRINT: "rev.device_fingerprint", LOGIN_REQUEST_TOKEN: "rev.login_request_token", } as const; export const COOKIE_DURATIONS = { SESSION: 60 * 60 * 24 * 7, // 7 days in seconds DEVICE_FINGERPRINT: 60 * 60 * 24 * 365, // 1 year in seconds LOGIN_REQUEST: 60 * 15, // 15 minutes in seconds } as const; export const TOKEN_DURATIONS = { EMAIL_VERIFICATION: 60 * 60 * 24, // 24 hours in seconds PASSWORD_RESET: 60 * 60, // 1 hour in seconds } as const; // Base cookie options (all cookies share these) const baseCookieOptions = { httpOnly: true, secure: true, sameSite: "lax" as const, path: "/", }; export const COOKIE_OPTIONS = { session: { ...baseCookieOptions, maxAge: COOKIE_DURATIONS.SESSION }, device: { ...baseCookieOptions, maxAge: COOKIE_DURATIONS.DEVICE_FINGERPRINT, }, loginRequest: { ...baseCookieOptions, maxAge: COOKIE_DURATIONS.LOGIN_REQUEST, }, } as const; /** * Cookie options type for setCookie function */ export interface CookieOptions { httpOnly?: boolean; secure?: boolean; sameSite?: "strict" | "lax" | "none"; path?: string; maxAge?: number; } /** * Parse cookie string and get a specific cookie value */ export const getCookie = ( headers: Headers, name: string, ): string | undefined => { const cookieHeader = headers.get("Cookie"); if (!cookieHeader) { return undefined; } const cookies = cookieHeader.split(";").map((c) => c.trim()); for (const cookie of cookies) { const [cookieName, ...valueParts] = cookie.split("="); if (cookieName === name) { return valueParts.join("="); } } return undefined; }; /** * Set a cookie in the response headers */ export const setCookie = ( headers: Headers, name: string, value: string, options: CookieOptions, ): void => { const parts = [`${name}=${value}`]; if (options.httpOnly) { parts.push("HttpOnly"); } if (options.secure) { parts.push("Secure"); } if (options.sameSite) { parts.push(`SameSite=${options.sameSite}`); } if (options.path) { parts.push(`Path=${options.path}`); } if (options.maxAge) { parts.push(`Max-Age=${String(options.maxAge)}`); } headers.append("Set-Cookie", parts.join("; ")); }; /** * Delete a cookie by setting it to expire immediately */ export const deleteCookie = (headers: Headers, name: string): void => { headers.append("Set-Cookie", `${name}=; Path=/; Max-Age=0`); };