aboutsummaryrefslogtreecommitdiffstats
path: root/src/api/dashboard/login.html
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-11-07 22:35:22 +0100
committertv <tv@krebsco.de>2026-11-07 22:35:22 +0100
commit56a3dca1874fba17c3d174be451af9478b3bfb9d (patch)
tree819e3a1d4902c6847b429dd69269ef475d067a45 /src/api/dashboard/login.html
parent7323e8ed8598cda9784493aa3334d947120b53d8 (diff)
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).
Diffstat (limited to 'src/api/dashboard/login.html')
-rw-r--r--src/api/dashboard/login.html18
1 files changed, 18 insertions, 0 deletions
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.";