diff --git a/dashboard/backend/config.py b/dashboard/backend/config.py index 4c76cdd..0fa176e 100644 --- a/dashboard/backend/config.py +++ b/dashboard/backend/config.py @@ -42,7 +42,7 @@ TELEGRAM_CHAT_ID = os.environ.get("TELEGRAM_CHAT_ID", "") TELEGRAM_PROXY = os.environ.get("TELEGRAM_PROXY", "") WEBAUTHN_RP_ID = os.environ.get("WEBAUTHN_RP_ID", "nas.jimmygan.com") -WEBAUTHN_ORIGINS = os.environ.get("WEBAUTHN_ORIGIN", "https://nas.jimmygan.com,https://nas.jimmygan.com:8443").split(",") +WEBAUTHN_ORIGINS = os.environ.get("WEBAUTHN_ORIGINS", "https://nas.jimmygan.com,https://nas.jimmygan.com:8443").split(",") # CORS CORS_ORIGINS = os.environ.get("CORS_ORIGINS", "https://nas.jimmygan.com,https://nas.jimmygan.com:8443").split(",") diff --git a/dashboard/frontend/src/lib/api.js b/dashboard/frontend/src/lib/api.js index de188bc..66e3ed8 100644 --- a/dashboard/frontend/src/lib/api.js +++ b/dashboard/frontend/src/lib/api.js @@ -23,19 +23,46 @@ export function setRefreshToken() {} let refreshPromise = null; +function getLegacyRefreshToken() { + return localStorage.getItem("refresh_token") || ""; +} + +function clearLegacyRefreshToken() { + localStorage.removeItem("refresh_token"); +} + +async function requestRefresh(body) { + const r = await fetch(BASE + "/auth/refresh", { + method: "POST", + headers: { "Content-Type": "application/json" }, + credentials: "same-origin", + body: body ? JSON.stringify(body) : undefined, + }); + if (!r.ok) return false; + const data = await r.json(); + setToken(data.access_token || ""); + return !!data.access_token; +} + export async function tryRefreshSession() { if (refreshPromise) return refreshPromise; refreshPromise = (async () => { try { - const r = await fetch(BASE + "/auth/refresh", { - method: "POST", - headers: { "Content-Type": "application/json" }, - credentials: "same-origin", - }); - if (!r.ok) return false; - const data = await r.json(); - setToken(data.access_token || ""); - return !!data.access_token; + const cookieRefreshed = await requestRefresh(); + if (cookieRefreshed) { + clearLegacyRefreshToken(); + return true; + } + + const legacyRefreshToken = getLegacyRefreshToken(); + if (!legacyRefreshToken) return false; + const legacyRefreshed = await requestRefresh({ refresh_token: legacyRefreshToken }); + if (legacyRefreshed) { + clearLegacyRefreshToken(); + return true; + } + clearLegacyRefreshToken(); + return false; } catch { return false; } finally { diff --git a/dashboard/frontend/src/routes/Settings.svelte b/dashboard/frontend/src/routes/Settings.svelte index 2f2dd7d..c3ec770 100644 --- a/dashboard/frontend/src/routes/Settings.svelte +++ b/dashboard/frontend/src/routes/Settings.svelte @@ -49,6 +49,9 @@ opts.excludeCredentials = opts.excludeCredentials.map(c => ({ ...c, id: base64urlToBuffer(c.id) })); } const cred = await navigator.credentials.create({ publicKey: opts }); + if (!cred) { + throw new Error("Passkey creation was cancelled"); + } const name = prompt("Name this passkey (e.g. MacBook, iPhone):", "Passkey") || "Passkey"; const body = { id: cred.id, @@ -64,9 +67,11 @@ passkeyMsg = "Passkey registered successfully"; await loadPasskeys(); } catch (e) { + console.error("Passkey registration error:", e); passkeyErr = e.body?.detail || e.message || "Failed to register passkey"; + } finally { + passkeyLoading = false; } - passkeyLoading = false; } async function clearPasskeys() {