PageSourceSearch

https://kwickey.com/js/components/auth.js?v=d5530e79454b

js kwickey.com collected 2026-10-08 11:58:20 UTC 3,836 bytes, 108 lines download raw bytes

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.