1/** 2 * Auth state â central place for the user's session token. 3 * 4 * The token is stored in a first-party cookie (`c.token`), not in localStorage. 5 * That reduces exposure to trivial XSS token theft via `localStorage.getItem`. 6 * 7 * Note: cookies set from JavaScript cannot be HttpOnly. True HttpOnly session 8 * cookies would require the API (Xano) to emit `Set-Cookie` on login/signup. 9 * Until then, keep XSS surfaces low (CSP) and never log or echo the token. 10 * 11 * Reading migrates any legacy `kwickey_auth_token` from localStorage into the 12 * cookie once, then deletes the localStorage entry. 13 * 14 * Events: 15 * document.dispatchEvent(new CustomEvent('auth:change', { detail: { token } })) 16 */ 17 18const LEGACY_STORAGE_KEY = 'kwickey_auth_token'; 19const COOKIE_NAME = 'c.token'; 20 21// 24h â matches Xano's `expiration = 86400` in /auth/login and /auth/signup. 22const COOKIE_MAX_AGE = 86400; 23 24/** @returns {string | null} */ 25export function getToken() { 26 const fromCookie = readCookie(COOKIE_NAME); 27 if (fromCookie) return fromCookie; 28 29 try { 30 const legacy = localStorage.getItem(LEGACY_STORAGE_KEY); 31 if (legacy) { 32 writeCookie(COOKIE_NAME, legacy, { maxAge: COOKIE_MAX_AGE }); 33 localStorage.removeItem(LEGACY_STORAGE_KEY); 34 return legacy; 35 } 36 } catch (_) { /* private mode / blocked storage */ } 37 38 return null; 39} 40 41/** @returns {boolean} */ 42export function isAuthenticated() { 43 return !!getToken(); 44} 45 46/** 47 * Persist a new token (after a successful login or signup). 48 * @param {string} token 49 */ 50export function setToken(token) { 51 if (!token) return; 52 deleteCookie(COOKIE_NAME); 53 writeCookie(COOKIE_NAME, token, { maxAge: COOKIE_MAX_AGE }); 54 try { localStorage.removeItem(LEGACY_STORAGE_KEY); } catch (_) { /* ignore */ } 55 document.dispatchEvent(new CustomEvent('auth:change', { detail: { token } })); 56} 57 58/** Log the user out. */ 59export function clearToken() { 60 deleteCookie(COOKIE_NAME); 61 try { localStorage.removeItem(LEGACY_STORAGE_KEY); } catch (_) { /* ignore */ } 62 document.dispatchEvent(new CustomEvent('auth:change', { detail: { token: null } })); 63} 64 65/* ââââââââââââââââ Cookie helpers ââââââââââââââââ */ 66 67function cookieDomainSuffix() { 68 const host = typeof location !== 'undefined' ? location.hostname : ''; 69 if (host === 'kwickey.com' || host.endsWith('.kwickey.com')) return '; Domain=.kwickey.com'; 70 if (host === 'lost-found.services' || host.endsWith('.lost-found.services')) return '; Domain=.lost-found.services'; 71 return ''; 72} 73 74function writeCookie(name, value, { maxAge, sameSite = 'Lax' } = {}) { 75 const parts = [ 76 `${name}=${encodeURIComponent(value)}`, 77 'Path=/', 78 `SameSite=${sameSite}`, 79 cookieDomainSuffix() 80 ]; 81 if (typeof maxAge === 'number') parts.push(`Max-Age=${maxAge}`); 82 if (location.protocol === 'https:') parts.push('Secure'); 83 document.cookie = parts.join('; '); 84} 85 86function readCookie(name) { 87 const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); 88 const match = document.cookie.match(new RegExp('(?:^|;\\s*)' + escaped + '=([^;]*)')); 89 return match ? decodeURIComponent(match[1]) : null; 90} 91 92/** 93 * Expire every variant of a cookie name (host-only + Domain=â¦, Secure or not). 94 * Needed because older builds set `c.token` without Domain; delete must match how it was set. 95 */ 96function deleteCookie(name) { 97 const variants = [ 98 `Path=/; Max-Age=0; SameSite=Lax`, 99 `Path=/; Max-Age=0; SameSite=Lax; Secure` 100 ]; 101 for (const domain of ['.kwickey.com', 'kwickey.com', '.lost-found.services', 'lost-found.services']) { 102 variants.push(`Path=/; Max-Age=0; SameSite=Lax; Domain=${domain}`); 103 variants.push(`Path=/; Max-Age=0; SameSite=Lax; Domain=${domain}; Secure`); 104 } 105 for (const attrs of variants) { 106 document.cookie = `${name}=; ${attrs}`; 107 } 108}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.