const AUTH_KEY = 'aetherforge_auth'; const AUTH_EXPIRED_KEY = 'aetherforge_auth_expired'; export const AETHERFORGE_CLIENT_HEADER = 'X-AetherForge-Client'; export const AETHERFORGE_CLIENT_VALUE = 'dashboard'; /** UTF-8-safe Basic auth token (username:password) for Authorization header. */ export function encodeBasicToken(username: string, password: string): string { const bytes = new TextEncoder().encode(`${username}:${password}`); let binary = ''; for (let i = 0; i < bytes.length; i++) { binary += String.fromCharCode(bytes[i]); } return btoa(binary); } function readAuthStorage(): string | null { try { const session = sessionStorage.getItem(AUTH_KEY); if (session) return session; } catch { /* sessionStorage blocked */ } try { return localStorage.getItem(AUTH_KEY); } catch { return null; } } function writeAuthStorage(token: string) { try { sessionStorage.setItem(AUTH_KEY, token); } catch { /* ignore */ } try { localStorage.setItem(AUTH_KEY, token); } catch { /* ignore */ } } function removeAuthStorage() { try { sessionStorage.removeItem(AUTH_KEY); } catch { /* ignore */ } try { localStorage.removeItem(AUTH_KEY); } catch { /* ignore */ } } export function getStoredAuth(): string | null { return readAuthStorage(); } export function setStoredAuth(username: string, password: string, opts?: { silent?: boolean }) { const token = encodeBasicToken(username, password); writeAuthStorage(token); if (!opts?.silent) { window.dispatchEvent(new Event('aetherforge-auth')); } } export function clearStoredAuth(opts?: { silent?: boolean; expired?: boolean }) { if (opts?.expired) { try { sessionStorage.setItem(AUTH_EXPIRED_KEY, '1'); } catch { /* ignore */ } } removeAuthStorage(); if (!opts?.silent) { window.dispatchEvent(new Event('aetherforge-auth')); } } /** True once after a 401 cleared stored credentials; consumed by SessionGate login UI. */ export function consumeAuthExpiredFlag(): boolean { try { if (sessionStorage.getItem(AUTH_EXPIRED_KEY)) { sessionStorage.removeItem(AUTH_EXPIRED_KEY); return true; } } catch { /* ignore */ } return false; } export function authHeaders(): Record { const headers: Record = { [AETHERFORGE_CLIENT_HEADER]: AETHERFORGE_CLIENT_VALUE, }; const token = getStoredAuth(); if (token) { headers.Authorization = `Basic ${token}`; } return headers; }