Security UI: confirm-password field for admin password + recovery reset
Mirror the PIN setup's confirm step so a typo can't silently set a password you can't reproduce. Both the Step 1 admin password (Settings) and the forgot-password reset (login screen) now require entering it twice and reject a mismatch before saving. 64 integrity tests pass.
This commit is contained in:
parent
2bb9bc1357
commit
09d4bbc530
2 changed files with 10 additions and 2 deletions
|
|
@ -75,7 +75,8 @@
|
|||
<div id="recovery-answer-section" style="display: none;">
|
||||
<p id="recovery-question-text" class="launch-pin-subtitle" style="margin-top: 12px; font-weight: 600;"></p>
|
||||
<input type="text" id="recovery-answer" class="launch-pin-input" placeholder="Your answer" autocomplete="off" maxlength="120" style="margin-bottom: 8px; letter-spacing: normal;">
|
||||
<input type="password" id="recovery-new-password" class="launch-pin-input" placeholder="New password (min 6)" autocomplete="new-password" maxlength="200" style="letter-spacing: normal;" onkeydown="if(event.key==='Enter') submitRecoveryReset()">
|
||||
<input type="password" id="recovery-new-password" class="launch-pin-input" placeholder="New password (min 6)" autocomplete="new-password" maxlength="200" style="margin-bottom: 8px; letter-spacing: normal;">
|
||||
<input type="password" id="recovery-new-password-confirm" class="launch-pin-input" placeholder="Confirm new password" autocomplete="new-password" maxlength="200" style="letter-spacing: normal;" onkeydown="if(event.key==='Enter') submitRecoveryReset()">
|
||||
<button class="launch-pin-submit" onclick="submitRecoveryReset()">Reset password</button>
|
||||
</div>
|
||||
<p id="recovery-error" class="launch-pin-error" style="display: none;"></p>
|
||||
|
|
@ -6039,7 +6040,8 @@
|
|||
|
||||
<div class="form-group">
|
||||
<label>Step 1 — Admin login password:</label>
|
||||
<input type="password" id="security-login-password" placeholder="Set admin login password" maxlength="200" autocomplete="new-password" style="margin: 6px 0;">
|
||||
<input type="password" id="security-login-password" placeholder="Enter password (min 6)" maxlength="200" autocomplete="new-password" style="margin: 6px 0;">
|
||||
<input type="password" id="security-login-password-confirm" placeholder="Confirm password" maxlength="200" autocomplete="new-password" style="margin-bottom: 6px;">
|
||||
<button class="auth-button" onclick="saveLoginPassword()">Save Password</button>
|
||||
<p id="security-login-password-msg" class="setting-help-text" style="margin-top: 6px; display: none;"></p>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -484,11 +484,13 @@ async function submitRecoveryReset() {
|
|||
const username = (document.getElementById('recovery-username')?.value || '').trim();
|
||||
const answer = document.getElementById('recovery-answer')?.value || '';
|
||||
const newPassword = document.getElementById('recovery-new-password')?.value || '';
|
||||
const confirmPassword = document.getElementById('recovery-new-password-confirm')?.value || '';
|
||||
const errEl = document.getElementById('recovery-error');
|
||||
const showErr = (m) => { if (errEl) { errEl.textContent = m; errEl.style.display = 'block'; } };
|
||||
if (errEl) errEl.style.display = 'none';
|
||||
if (!answer || !newPassword) { showErr('Enter your answer and a new password'); return; }
|
||||
if (newPassword.length < 6) { showErr('New password must be at least 6 characters'); return; }
|
||||
if (newPassword !== confirmPassword) { showErr('Passwords do not match'); return; }
|
||||
try {
|
||||
const res = await fetch('/api/auth/recovery-reset', {
|
||||
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
||||
|
|
@ -566,8 +568,10 @@ function showLaunchPinScreen() {
|
|||
|
||||
async function saveLoginPassword() {
|
||||
const input = document.getElementById('security-login-password');
|
||||
const confirmInput = document.getElementById('security-login-password-confirm');
|
||||
const msg = document.getElementById('security-login-password-msg');
|
||||
const password = input?.value || '';
|
||||
const confirm = confirmInput?.value || '';
|
||||
const show = (text, ok) => {
|
||||
if (!msg) return;
|
||||
msg.textContent = text;
|
||||
|
|
@ -575,6 +579,7 @@ async function saveLoginPassword() {
|
|||
msg.style.display = 'block';
|
||||
};
|
||||
if (!password || password.length < 6) { show('Password must be at least 6 characters', false); return; }
|
||||
if (password !== confirm) { show('Passwords do not match', false); return; }
|
||||
try {
|
||||
const res = await fetch('/api/profiles/1/set-password', {
|
||||
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
||||
|
|
@ -584,6 +589,7 @@ async function saveLoginPassword() {
|
|||
if (data.success) {
|
||||
show('Admin login password saved', true);
|
||||
if (input) input.value = '';
|
||||
if (confirmInput) confirmInput.value = '';
|
||||
updateRequireLoginGate(true); // Step 1 done → unlock Step 3
|
||||
}
|
||||
else show(data.error || 'Failed to save password', false);
|
||||
|
|
|
|||
Loading…
Reference in a new issue