Rework auth to allow smooth switching

This commit is contained in:
Zerebos
2026-05-15 23:50:19 -04:00
parent 062662781a
commit 0bea9c22cb
3 changed files with 86 additions and 22 deletions
+60 -10
View File
@@ -9,20 +9,53 @@ export class AuthRequiredError extends Error {
}
}
const TOKEN_KEY = "moku_access_token";
let _accessToken: string | null = sessionStorage.getItem(TOKEN_KEY);
const TOKEN_KEY = "moku_access_token_v2";
const LEGACY_TOKEN_KEY = "moku_access_token";
interface StoredAccessToken {
base: string;
token: string;
}
let _accessToken: string | null = null;
let _accessTokenBase: string | null = null;
export const uiAuth = {
getToken: () => _accessToken,
setToken: (t: string) => { _accessToken = t; sessionStorage.setItem(TOKEN_KEY, t); },
clearToken: () => { _accessToken = null; sessionStorage.removeItem(TOKEN_KEY); },
getToken: () => {
const base = getServerBase();
if (_accessToken && _accessTokenBase === base) return _accessToken;
const stored = readStoredToken();
if (!stored) return null;
if (stored.base !== base) {
sessionStorage.removeItem(TOKEN_KEY);
_accessToken = null;
_accessTokenBase = null;
return null;
}
_accessToken = stored.token;
_accessTokenBase = stored.base;
return _accessToken;
},
setToken: (t: string) => {
const base = getServerBase();
_accessToken = t;
_accessTokenBase = base;
sessionStorage.setItem(TOKEN_KEY, JSON.stringify({ base, token: t }));
sessionStorage.removeItem(LEGACY_TOKEN_KEY);
},
clearToken: () => {
_accessToken = null;
_accessTokenBase = null;
sessionStorage.removeItem(TOKEN_KEY);
sessionStorage.removeItem(LEGACY_TOKEN_KEY);
},
};
export const authSession = {
clearTokens() { uiAuth.clearToken(); },
hasSession(): boolean {
const mode = store.settings.serverAuthMode ?? "NONE";
if (mode === "UI_LOGIN") return _accessToken !== null;
if (mode === "UI_LOGIN") return uiAuth.getToken() !== null;
return true;
},
};
@@ -32,6 +65,22 @@ function getServerBase(): string {
return typeof url === "string" && url.trim() ? url.replace(/\/$/, "") : "http://127.0.0.1:4567";
}
function readStoredToken(): StoredAccessToken | null {
const raw = sessionStorage.getItem(TOKEN_KEY);
if (raw) {
try {
const parsed = JSON.parse(raw);
if (typeof parsed?.base === "string" && typeof parsed?.token === "string")
return { base: parsed.base, token: parsed.token };
} catch {}
}
const legacy = sessionStorage.getItem(LEGACY_TOKEN_KEY);
if (legacy && legacy.trim()) {
return { base: getServerBase(), token: legacy.trim() };
}
return null;
}
function timeoutSignal(ms: number): AbortSignal {
const controller = new AbortController();
setTimeout(() => controller.abort(), ms);
@@ -100,7 +149,7 @@ export async function loginUI(user: string, pass: string): Promise<void> {
const token: string | undefined = json?.data?.login?.accessToken;
if (!token) throw new Error(json?.errors?.[0]?.message ?? "Login failed");
uiAuth.setToken(token);
updateSettings({ serverAuthMode: "UI_LOGIN" });
updateSettings({ serverAuthMode: "UI_LOGIN", serverAuthUser: user, serverAuthPass: "" });
}
export async function loginBasic(user: string, pass: string): Promise<void> {
@@ -123,8 +172,9 @@ export async function probeServer(): Promise<"ok" | "auth_required" | "unreachab
const base = getServerBase();
const mode = store.settings.serverAuthMode ?? "NONE";
const s = store.settings;
const token = uiAuth.getToken();
if (mode === "UI_LOGIN" && !_accessToken) return "auth_required";
if (mode === "UI_LOGIN" && !token) return "auth_required";
try {
const headers: Record<string, string> = { "Content-Type": "application/json" };
@@ -132,8 +182,8 @@ export async function probeServer(): Promise<"ok" | "auth_required" | "unreachab
const user = s.serverAuthUser?.trim() ?? "";
const pass = s.serverAuthPass?.trim() ?? "";
if (user && pass) Object.assign(headers, basicHeader(user, pass));
} else if (mode === "UI_LOGIN" && _accessToken) {
Object.assign(headers, bearerHeader(_accessToken));
} else if (mode === "UI_LOGIN" && token) {
Object.assign(headers, bearerHeader(token));
}
const res = await fetch(`${base}/api/graphql`, {