Show management links and a signed-in indicator when logged in
Add GET /api/session, an unguarded probe that reports whether the request
carries valid credentials. It returns 200 with {"authenticated":bool} and never
sends WWW-Authenticate, so the static public pages can check login state without
triggering the browser's Basic-auth dialog. To keep it from being a
password-guessing oracle that bypasses the /upload limiter, a request with no
credentials is answered cheaply as false, while wrong credentials go through the
same failed-attempt limiter (429 once drained).
static/session.js, loaded by every page template, calls it and, only when
authenticated, builds a fixed toolbar: Upload / Manage links, an "Edit this"
link on photo pages (via <body data-slug>, deep-linking to /manage?edit=<slug>,
which scrolls to and focuses that row), a signed-in indicator, and a log-out
control. Logged-out visitors see nothing and get no dialog.
Log out is best-effort: HTTP Basic has no reliable sign-out (the browser caches
credentials until the window closes), so it overwrites the cached credential and
reloads, which works in some browsers but not all. A dependable logout would
need cookie sessions.
This commit is contained in:
1 parent
c4aa345914
commit
94859fb0d6
13 files changed
+163
-1
No files matched your search
@@ -0,0 +1,65 @@
|
||||
// Shows a management toolbar + signed-in indicator on every page when the
|
||||
// browser holds valid credentials. The public gallery is static and carries no
|
||||
// markup for this; the bar is built here only after /api/session confirms the
|
||||
// viewer is authenticated, so logged-out visitors see nothing and get no login
|
||||
// dialog.
|
||||
//
|
||||
// A page may set <body data-slug="..."> (the photo pages do) to get an
|
||||
// "Edit this" link straight to that post in /manage.
|
||||
(function () {
|
||||
fetch("/api/session", { headers: { Accept: "application/json" }, cache: "no-store" })
|
||||
.then((r) => (r.ok ? r.json() : { authenticated: false }))
|
||||
.then((s) => {
|
||||
if (s && s.authenticated) buildBar();
|
||||
})
|
||||
.catch(() => {});
|
||||
|
||||
function el(tag, cls, text) {
|
||||
const e = document.createElement(tag);
|
||||
if (cls) e.className = cls;
|
||||
if (text != null) e.textContent = text;
|
||||
return e;
|
||||
}
|
||||
|
||||
function buildBar() {
|
||||
document.body.classList.add("authed");
|
||||
|
||||
const bar = el("div", "session-bar");
|
||||
const path = location.pathname.replace(/\/$/, "");
|
||||
|
||||
const link = (href, label) => {
|
||||
const a = el("a", null, label);
|
||||
a.href = href;
|
||||
bar.appendChild(a);
|
||||
};
|
||||
|
||||
if (path !== "/upload") link("/upload", "Upload");
|
||||
if (path !== "/manage") link("/manage", "Manage");
|
||||
|
||||
const slug = document.body.dataset.slug;
|
||||
if (slug) link("/manage?edit=" + encodeURIComponent(slug), "Edit this");
|
||||
|
||||
bar.appendChild(el("span", "session-dot", "● signed in"));
|
||||
|
||||
// Best-effort logout. HTTP Basic has no real sign-out — the browser caches
|
||||
// credentials and clears them reliably only when the window closes — so
|
||||
// this overwrites the cached credential with a throwaway one and reloads.
|
||||
// It works in some browsers and not others; a dependable logout would need
|
||||
// cookie sessions.
|
||||
const out = el("a", "session-out", "log out");
|
||||
out.href = "#";
|
||||
out.title = "Best-effort — HTTP Basic auth has no reliable logout";
|
||||
out.addEventListener("click", (e) => {
|
||||
e.preventDefault();
|
||||
fetch("/api/session", {
|
||||
headers: { Authorization: "Basic " + btoa("logout:" + Date.now()) },
|
||||
cache: "no-store",
|
||||
})
|
||||
.catch(() => {})
|
||||
.finally(() => setTimeout(() => location.reload(), 150));
|
||||
});
|
||||
bar.appendChild(out);
|
||||
|
||||
document.body.appendChild(bar);
|
||||
}
|
||||
})();
|
||||
Reference in new issue
Block a user