From 56a3dca1874fba17c3d174be451af9478b3bfb9d Mon Sep 17 00:00:00 2001 From: tv Date: Sat, 7 Nov 2026 22:35:22 +0100 Subject: dashboard: a token handed over to another member's dashboard Opening another member's dashboard passes a token reading only, for five minutes, in the URL's fragment; its login page begins a session with it. Tokens may be asked for with a shorter lifetime. Auth plan M3 (hand-off). --- src/api/dashboard/login.html | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'src/api/dashboard/login.html') diff --git a/src/api/dashboard/login.html b/src/api/dashboard/login.html index 77668ee..e09d31c 100644 --- a/src/api/dashboard/login.html +++ b/src/api/dashboard/login.html @@ -73,6 +73,24 @@ document.getElementById("token").addEventListener("submit", async event => { why.textContent = ""; try { await begin(event.target.token.value.trim()); } catch (error) { failed(error); } }); +// Opened from another member's dashboard (`handOff`), sent here for want of a session: +// its token, reading only, begins one; the rest goes on to the dashboard. +(async () => { + const HAND_OFF = "#continuum="; + if (!location.hash.startsWith(HAND_OFF)) return; + const fragment = location.hash; + try { history.replaceState(null, "", location.pathname + location.search); } catch (_) {} + let handed; + try { handed = JSON.parse(decodeURIComponent(fragment.slice(HAND_OFF.length))); } catch (_) { return; } + if (!handed || typeof handed.token !== "string") return; + try { + await call("auth/session", "POST", "Bearer " + handed.token); + delete handed.token; + location.replace("admin/dashboard" + HAND_OFF + encodeURIComponent(JSON.stringify(handed))); + } catch (error) { + why.textContent = "The token handed over isn't taken (" + error.message + "): log in."; + } +})(); document.getElementById("out").addEventListener("click", async () => { await fetch("auth/session", { method: "DELETE", credentials: "same-origin" }).catch(() => {}); why.textContent = "Logged out of this browser."; -- cgit v1.3.1