OTP security hardened

This commit is contained in:
2026-08-26 18:24:02 +02:00
parent c3c3c8e1a6
commit 580c2a4257
13 changed files with 227 additions and 13 deletions
+26
View File
@@ -152,6 +152,11 @@ function renderUsers(users) {
privilegeLabel.append(privilegeCheckbox, document.createTextNode(' Administrator'));
row.append(label, privilegeLabel);
if (!isCurrentUser) {
const otpButton = document.createElement('button');
otpButton.type = 'button';
otpButton.textContent = 'Reset OTP';
otpButton.addEventListener('click', () => resetUserOtp(user, otpButton));
row.append(otpButton);
const button = document.createElement('button');
button.type = 'button';
button.className = 'danger-button';
@@ -163,6 +168,27 @@ function renderUsers(users) {
}));
}
async function resetUserOtp(user, button) {
if (!window.confirm(`Disable OTP for ${user.username}?`)) return;
button.disabled = true;
const status = document.querySelector('#user-status');
try {
const response = await fetch(`/api/admin/users/${encodeURIComponent(user.id)}/otp/reset`, {
method: 'POST',
headers: authHeaders(),
});
if (!response.ok) {
throw new Error(await responseError(response, `Request failed (${response.status})`));
}
status.textContent = `OTP disabled for ${user.username}.`;
status.style.color = '#94e2d5';
} catch (error) {
status.textContent = `Could not reset OTP for ${user.username}: ${error.message}`;
status.style.color = '#f38ba8';
button.disabled = false;
}
}
async function loadUsers() {
const response = await fetch('/api/admin/users', {headers: authHeaders()});
if (!response.ok) throw new Error('Could not load users');