Files
ssh-jumphost/static/js/login.js
2026-08-20 09:39:20 +02:00

86 lines
2.9 KiB
JavaScript

(() => {
"use strict";
const form = document.getElementById("login-form");
const passwordFields = document.getElementById("password-fields");
const totpFields = document.getElementById("totp-fields");
const enrollBox = document.getElementById("enroll-box");
const recoveryBox = document.getElementById("recovery-box");
const errorBox = document.getElementById("error-box");
const submitBtn = document.getElementById("submit-btn");
let pendingToken = null;
let mode = "password"; // password -> totp | enroll_start -> enroll_confirm -> done
function showError(msg) {
errorBox.textContent = msg;
}
async function postJson(url, body) {
const res = await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
credentials: "same-origin",
body: JSON.stringify(body),
});
const data = await res.json().catch(() => ({}));
if (!res.ok) {
throw new Error(data.detail || "Unbekannter Fehler");
}
return data;
}
form.addEventListener("submit", async (ev) => {
ev.preventDefault();
showError("");
try {
if (mode === "password") {
const username = document.getElementById("username").value.trim();
const password = document.getElementById("password").value;
const result = await postJson("/auth/login", { username, password });
pendingToken = result.pending_token;
passwordFields.classList.add("hidden");
totpFields.classList.remove("hidden");
if (!result.totp_enrolled) {
mode = "enroll_start";
const enroll = await postJson("/auth/totp/enroll/start", { pending_token: pendingToken });
document.getElementById("qr-img").src = "data:image/png;base64," + enroll.qr_png_base64;
enrollBox.classList.remove("hidden");
mode = "enroll_confirm";
submitBtn.textContent = "TOTP bestaetigen & einrichten";
} else {
mode = "totp";
submitBtn.textContent = "Code bestaetigen";
}
return;
}
if (mode === "totp") {
const code = document.getElementById("totp-code").value.trim();
await postJson("/auth/login/totp", { pending_token: pendingToken, code });
window.location.href = "/dashboard";
return;
}
if (mode === "enroll_confirm") {
const code = document.getElementById("totp-code").value.trim();
const result = await postJson("/auth/totp/enroll/confirm", { pending_token: pendingToken, code });
recoveryBox.classList.remove("hidden");
document.getElementById("recovery-codes").textContent = result.recovery_codes.join("\n");
submitBtn.textContent = "Weiter zum Dashboard";
mode = "done";
return;
}
if (mode === "done") {
window.location.href = "/dashboard";
}
} catch (err) {
showError(err.message);
}
});
})();