import { browser } from "$app/environment"; import { isDid, type Did } from "@atcute/lexicons/syntax"; import { safeReturnTo } from "$lib/auth/returnTo"; const PENDING_LOGIN_KEY = "tangled.pendingLogin"; const PENDING_CALLBACK_KEY = "tangled.pendingCallback"; const PENDING_LOGIN_MAX_AGE_MS = 10 * 60 * 1000; export interface PendingLogin { did: Did; handle: string; returnTo: string; at: number; } export interface PendingCallback { hash: string; login: PendingLogin; } const isPendingLogin = (value: unknown): value is PendingLogin => !!value && typeof value === "object" && isDid((value as PendingLogin).did) && typeof (value as PendingLogin).handle === "string" && typeof (value as PendingLogin).returnTo === "string" && typeof (value as PendingLogin).at === "number"; export const writePendingLogin = (login: Omit): void => { if (!browser) return; localStorage.setItem(PENDING_LOGIN_KEY, JSON.stringify({ ...login, at: Date.now() })); }; export const readPendingLogin = (): PendingLogin | null => { if (!browser) return null; try { const raw = localStorage.getItem(PENDING_LOGIN_KEY); if (!raw) return null; const parsed: unknown = JSON.parse(raw); if (!isPendingLogin(parsed)) return null; if (Date.now() - parsed.at > PENDING_LOGIN_MAX_AGE_MS) return null; return { ...parsed, returnTo: safeReturnTo(parsed.returnTo) }; } catch { return null; } }; export const clearPendingLogin = (): void => { if (!browser) return; localStorage.removeItem(PENDING_LOGIN_KEY); }; export const stashPendingCallback = (callback: PendingCallback): void => { if (!browser) return; sessionStorage.setItem(PENDING_CALLBACK_KEY, JSON.stringify(callback)); }; export const consumePendingCallback = (): PendingCallback | null => { if (!browser) return null; try { const raw = sessionStorage.getItem(PENDING_CALLBACK_KEY); if (!raw) return null; sessionStorage.removeItem(PENDING_CALLBACK_KEY); const parsed: unknown = JSON.parse(raw); if ( !parsed || typeof parsed !== "object" || typeof (parsed as PendingCallback).hash !== "string" || !isPendingLogin((parsed as PendingCallback).login) ) { return null; } const callback = parsed as PendingCallback; return { ...callback, login: { ...callback.login, returnTo: safeReturnTo(callback.login.returnTo) } }; } catch { return null; } };