fix(reauth): wrong master pw re-prompts inline instead of closing

Setting a PIN, enabling Quick Unlock, and generating a recovery key all
closed the reauth modal and showed a toast on the first wrong master
password — no retry. Export already looped inline; the others didn't.

Extracted that loop into reauthLoop(message): re-prompts with an inline
error + attempt count (5 tries), honours the 429 lockout, returns the
verified password or null. All four sensitive-action flows now share it, so
a mistyped master password keeps the modal up to try again.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
r-zakarya
2026-07-24 23:52:05 +01:00
parent b542106c76
commit 9b7d04ee46
3 changed files with 49 additions and 58 deletions
+39
View File
@@ -6022,6 +6022,45 @@ function closeReauth(ok) {
}
}
// Prompt for the master password and verify it via /reauth, RE-PROMPTING
// inline on a wrong entry (the modal stays up with an error + attempt count)
// instead of closing on the first miss. The single choke point for every
// sensitive-action reauth (export, PIN set, Quick Unlock, recovery) so they
// all behave the same. Returns the verified master password, or null when the
// user cancels or the attempt cap / server lockout is hit (already toasted).
async function reauthLoop(message) {
const MAX = 5;
let attempts = 0;
let lastError = '';
for (;;) {
const pwd = await askReauth(message, { error: lastError });
if (!pwd) return null; // cancelled
try {
const verifier = await computeVerifier(
pwd, state.salt, state.kdfIterations || 100000,
state.hashAlgo, state.argon2Params);
await api('/reauth', {
method: 'POST',
headers: authHeaders({ 'Content-Type': 'application/json' }),
body: JSON.stringify({ verifier }),
});
return pwd;
} catch (err) {
if (err.status === 429 && err.body && err.body.retry_after) {
toast('Account locked. Try again in ' +
Math.ceil(err.body.retry_after / 60) + ' min', 'warning');
return null;
}
attempts++;
if (attempts >= MAX) {
toast('Too many wrong attempts — try again later', 'error');
return null;
}
lastError = 'Wrong master password. Attempt ' + attempts + ' / ' + MAX + '.';
}
}
}
// ============================================================
// QUICK UNLOCK + PIN + RECOVERY KEY — extracted to js/app.unlock.js (§3.1),
// loaded AFTER this file (it assigns Bridge.onQuickUnlockResult / onPinResult