theseus/auth-prompt.html
Local Dev ab78535192 fix(theseus): prompt for HTTP authentication instead of showing the bare 401
Sites behind Basic/Digest auth (silentmode.st/guardian/admin) rendered the
server's 401 page because nothing listened for Electron's login event,
which cancels every challenge by default. A modal sign-in prompt now asks
for the credentials and answers the challenge; Cancel leaves the 401 page.
Concurrent challenges for the same host and realm share one prompt while it
is open, and a rejected answer re-prompts instead of replaying the same
credentials until Chromium gives up with ERR_TOO_MANY_RETRIES.

Also: THESEUS_NO_UPDATE_CHECK skips the release check, for throwaway dev
instances — the one-click install chip they show targets the real install.
2026-09-15 22:30:29 +02:00

77 lines
4 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Sign in</title>
<style>
:root { color-scheme: light dark;
--surface:#1c222c; --surface2:#0f1621; --line:rgba(255,255,255,.12);
--ink:#e7eaf1; --mut:#8b98a9; --dim:#5e6678; --acid:#d6ff3d; --danger:#f6768a; }
@media (prefers-color-scheme: light) {
:root { --surface:#ffffff; --surface2:#f1f4fa; --line:rgba(0,0,0,.12);
--ink:#1a1f2b; --mut:#5c6577; --dim:#8a93a5; --acid:#0AC18E; }
}
* { box-sizing: border-box; }
html, body { margin: 0; height: 100%; background: var(--surface); }
body { font: 13px/1.5 system-ui, -apple-system, Segoe UI, Roboto, sans-serif; color: var(--ink);
padding: 16px 18px 14px; display: flex; flex-direction: column; gap: 10px; }
.title { font-size: 15px; font-weight: 650; margin: 0; }
.origin { display: inline-flex; align-items: center; gap: 8px; max-width: 100%;
background: var(--surface2); border: 1px solid rgb(from var(--acid) r g b / .35); color: var(--acid);
border-radius: 8px; padding: 6px 10px;
font: 13px/1.3 ui-monospace, "Cascadia Code", Consolas, monospace; word-break: break-all; }
.origin .lbl { color: var(--dim); font: 11px system-ui, sans-serif; white-space: nowrap; }
.hint { color: var(--mut); font-size: 12px; }
.hint b { color: var(--ink); font-weight: 600; }
.warn { color: var(--danger); font-size: 12px; }
label { display: grid; gap: 4px; color: var(--mut); font-size: 12px; }
input { font: 13px system-ui, sans-serif; color: var(--ink); background: var(--surface2);
border: 1px solid var(--line); border-radius: 8px; padding: 7px 10px; outline: none; }
input:focus { border-color: var(--acid); }
.btns { display: flex; justify-content: flex-end; gap: 8px; margin-top: auto; padding-top: 4px; }
button { font: 13px system-ui, sans-serif; border-radius: 8px; padding: 7px 14px; cursor: pointer;
border: 1px solid var(--line); background: var(--surface2); color: var(--ink); }
button.primary { background: var(--acid); color: #0f1420; border-color: transparent; font-weight: 650; }
button:focus-visible { outline: 2px solid var(--acid); outline-offset: 1px; }
</style>
</head>
<body>
<h1 class="title" id="title">Sign in</h1>
<div class="origin"><span class="lbl" id="lbl">site</span><span id="origin"></span></div>
<div class="hint" id="hint"></div>
<div class="warn" id="warn" hidden>This connection is not encrypted — the password would be sent in plain text.</div>
<form id="f" autocomplete="off">
<label>Username <input id="user" type="text" autocomplete="username" spellcheck="false"></label>
<label style="margin-top:8px">Password <input id="pass" type="password" autocomplete="current-password"></label>
<div class="btns">
<button type="button" id="cancel">Cancel</button>
<button type="submit" class="primary" id="ok">Sign in</button>
</div>
</form>
<script>
const $ = (id) => document.getElementById(id);
let reqId = null, done = false;
function finish(creds) {
if (done || reqId == null) return;
done = true;
window.authPrompt.answer(reqId, creds);
}
window.authPrompt.onShow((req) => {
reqId = req.id; done = false;
$("title").textContent = req.isProxy ? "Proxy sign-in" : "Sign in";
$("lbl").textContent = req.isProxy ? "proxy" : "site";
$("origin").textContent = req.origin;
$("hint").innerHTML = req.realm
? "The " + (req.isProxy ? "proxy" : "site") + " says: <b></b>"
: "The " + (req.isProxy ? "proxy" : "site") + " requires a username and password.";
if (req.realm) $("hint").querySelector("b").textContent = req.realm;
$("warn").hidden = !req.insecure;
$("user").value = ""; $("pass").value = "";
$("user").focus();
});
$("f").addEventListener("submit", (e) => { e.preventDefault(); finish({ username: $("user").value, password: $("pass").value }); });
$("cancel").addEventListener("click", () => finish(null));
document.addEventListener("keydown", (e) => { if (e.key === "Escape") finish(null); });
</script>
</body>
</html>