export type AuthSession = { centerApiBase: string accessToken: string refreshToken: string tokenType: string expiresIn?: number user: { id: string email: string name: string } guilds: Array<{ nodeId: string name: string endpoint: string status: 'active' | 'offline' | 'revoked' }> guildAccessTokens: Array<{ guildNodeId: string token: string tokenType: string expiresIn?: number }> } const KEY = 'fabric.auth.session.v1' export function getAuthSession(): AuthSession | null { const raw = localStorage.getItem(KEY) if (!raw) return null try { return JSON.parse(raw) as AuthSession } catch { return null } } export function setAuthSession(session: AuthSession): void { localStorage.setItem(KEY, JSON.stringify(session)) } export function clearAuthSession(): void { localStorage.removeItem(KEY) } function parseJwtExpMs(token: string): number | null { const payload = token.split('.')[1] if (!payload) return null try { const decoded = JSON.parse(atob(payload)) as { exp?: number } if (!decoded.exp) return null return decoded.exp * 1000 } catch { return null } } export function isAccessTokenStale(token: string, leadMs = 60_000): boolean { const expMs = parseJwtExpMs(token) if (!expMs) return true return Date.now() + leadMs >= expMs }