import { browser } from "$app/env";
import { isDid, type Did } from "@atcute/lexicons/syntax";
import { safeReturnTo } from "#lib/auth/returnTo.ts";
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<PendingLogin, "at">): 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;
}
};