feat(webext): SecuBox login (Bearer) so the cookie-relay authenticates (ref #401)

The cookie-relay POST hits an authenticated dashboard endpoint. Add a small
login (SecuBox user/pass → /auth/login → JWT stored in extension storage,
sent as Authorization: Bearer). On 401 the popup reveals a login box and
retries automatically after login.
This commit is contained in:
CyberMind-FR 2026-05-28 11:08:54 +02:00
parent ea92aaee7a
commit b9c9b918cc
3 changed files with 59 additions and 3 deletions

View File

@ -48,6 +48,12 @@ li a:hover { color: var(--cyber-cyan); }
.btn:hover { border-color: var(--cyber-cyan); }
.btn-primary { border-color: var(--gold-hermetic); color: var(--gold-hermetic); }
.btn-primary:hover { background: var(--gold-hermetic); color: var(--cosmos-black); }
#login-box { margin-top: 6px; }
#login-box input {
width: 100%; padding: 6px 8px; margin: 3px 0;
background: #16161f; color: var(--text-primary);
border: 1px solid #2a2a3a; border-radius: 4px; font-family: inherit; font-size: 12px;
}
footer {
display: flex; justify-content: space-between;
padding: 10px 12px; border-top: 1px solid #1c1c28;

View File

@ -26,6 +26,11 @@
<h3>PeerTube import</h3>
<button id="relay-cookies" class="btn btn-primary">Send YouTube cookies → PeerTube</button>
<p id="relay-status" class="muted"></p>
<div id="login-box" class="hidden">
<input id="login-user" type="text" placeholder="SecuBox user" autocomplete="username">
<input id="login-pass" type="password" placeholder="password" autocomplete="current-password">
<button id="login-btn" class="btn">Log in &amp; retry</button>
</div>
</section>
<footer>

View File

@ -7,6 +7,8 @@ const DEFAULTS = {
modules: ["hub", "crowdsec", "waf", "wireguard", "peertube", "photoprism", "nextcloud"],
// PeerTube cookie-intake endpoint (backend lands with #401 P1.5 / #388).
cookieEndpoint: "/api/v1/peertube/import/cookies",
// SecuBox login endpoint (secubox-core JWT; any module's /auth/login works).
loginEndpoint: "/api/v1/peertube/auth/login",
};
async function getConfig() {
@ -14,6 +16,28 @@ async function getConfig() {
return { ...DEFAULTS, ...stored };
}
async function getToken() {
return (await api.storage.local.get({ token: "" })).token;
}
async function authHeaders() {
const t = await getToken();
return t ? { Authorization: "Bearer " + t } : {};
}
async function login(cfg, username, password) {
const r = await fetch(`${cfg.hubBase}${cfg.loginEndpoint}`, {
method: "POST", credentials: "include",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ username, password }),
});
if (!r.ok) throw new Error("login failed (" + r.status + ")");
const d = await r.json();
if (!d.access_token) throw new Error("no token in response");
await api.storage.local.set({ token: d.access_token });
return d.access_token;
}
function setDot(el, state) {
el.className = "dot dot-" + (state === "up" ? "up" : state === "down" ? "down" : "unknown");
}
@ -68,6 +92,7 @@ function toNetscape(cookies) {
async function relayCookies(cfg) {
const status = document.getElementById("relay-status");
const loginBox = document.getElementById("login-box");
if (!cfg.hubBase) { status.textContent = "Configure the hub first."; return; }
status.textContent = "Reading YouTube cookies…";
try {
@ -80,11 +105,17 @@ async function relayCookies(cfg) {
status.textContent = `Sending ${cookies.length} cookies…`;
const r = await fetch(`${cfg.hubBase}${cfg.cookieEndpoint}`, {
method: "POST", credentials: "include",
headers: { "Content-Type": "text/plain" }, body,
headers: { "Content-Type": "text/plain", ...(await authHeaders()) }, body,
});
if (r.status === 401 || r.status === 403) {
status.textContent = "SecuBox login required:";
loginBox.classList.remove("hidden");
return;
}
loginBox.classList.add("hidden");
status.textContent = r.ok
? `Sent ${cookies.length} cookies — retry the PeerTube import.`
: `Backend returned ${r.status} (cookie endpoint not deployed yet?).`;
? `Sent ${cookies.length} cookies — PeerTube is restarting; retry the import shortly.`
: `Backend returned ${r.status}.`;
} catch (e) {
status.textContent = "Relay failed: " + (e?.message || e);
}
@ -97,6 +128,20 @@ document.addEventListener("DOMContentLoaded", async () => {
document.getElementById("open-options-footer").addEventListener("click", openOpts);
document.getElementById("refresh").addEventListener("click", () => renderModules(cfg));
document.getElementById("relay-cookies").addEventListener("click", () => relayCookies(cfg));
document.getElementById("login-btn").addEventListener("click", async () => {
const status = document.getElementById("relay-status");
const u = document.getElementById("login-user").value.trim();
const p = document.getElementById("login-pass").value;
if (!u || !p) { status.textContent = "Enter SecuBox username + password."; return; }
status.textContent = "Logging in…";
try {
await login(cfg, u, p);
document.getElementById("login-pass").value = "";
await relayCookies(cfg); // retry now that we have a token
} catch (e) {
status.textContent = "Login failed: " + (e?.message || e);
}
});
const hub = document.getElementById("open-hub");
hub.href = cfg.hubBase || "#";