86 lines
2.9 KiB
JavaScript
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);
|
|
}
|
|
});
|
|
})();
|