fix(auth): expire stale MFA forms before accepting codes

This commit is contained in:
Clintchiz committed 2026-08-26 19:17:48 +05:30
1 parent 298e34ef2e
commit 0c77de93b4
8 files changed
+82 -11

No files matched your search

+33 -6
View File
@@ -413,7 +413,32 @@
const pending = new Map();
let expired = false;
let disposed = false;
let expiryTimer;
const expiresAt = Number(saved?.mfaExpiresAt);
const expire = () => {
if (disposed) return;
expired = true;
window.clearTimeout(expiryTimer);
sessionStorage.removeItem(MFA_STORAGE_KEY);
if (tokenInput) tokenInput.value = "";
form?.querySelectorAll("input, button").forEach((control) => {
control.disabled = true;
});
setMessage(
status,
"Your sign-in request has expired. Sign in again to get a fresh verification request.",
true,
);
const restart = element.querySelector("[data-auth-mfa-restart]");
if (restart) restart.hidden = false;
};
const checkExpiry = () => {
// Old stored challenges have no verifiable deadline: require fresh sign-in.
if (saved?.mfaToken && (!Number.isFinite(expiresAt) || expiresAt <= Date.now())) expire();
return expired;
};
const restoreToken = () => {
checkExpiry();
if (!expired && tokenInput && !tokenInput.value && saved?.mfaToken)
tokenInput.value = String(saved.mfaToken);
};
@@ -433,6 +458,7 @@
const sendCode = async (selected) => {
if (disposed || expired || !isOtp(selected)) return;
restoreToken();
if (expired) return;
if (!tokenInput?.value) {
setMessage(status, "Sign-in verification has expired. Start again.", true);
const restart = element.querySelector("[data-auth-mfa-restart]");
@@ -476,10 +502,11 @@
);
} finally {
pending.delete(selected);
if (!disposed && method() === selected) setFormBusy(form, false);
if (!disposed && !expired && method() === selected) setFormBusy(form, false);
}
};
const updateMethod = () => {
if (checkExpiry()) return;
const selected = method();
setFormBusy(form, false);
if (challengeInput) challengeInput.value = "";
@@ -524,13 +551,12 @@
};
const onError = (event) => {
if (event.target !== form || event.detail?.code !== "mfa-transaction-expired") return;
expired = true;
sessionStorage.removeItem(MFA_STORAGE_KEY);
if (tokenInput) tokenInput.value = "";
const restart = element.querySelector("[data-auth-mfa-restart]");
if (restart) restart.hidden = false;
expire();
};
updateMethod();
if (!expired && Number.isFinite(expiresAt)) {
expiryTimer = window.setTimeout(expire, Math.max(0, expiresAt - Date.now()));
}
// Capture native change before the shared Radio emits its component output.
form?.addEventListener("change", onMethodChange, true);
form?.addEventListener("submit", onSubmit, true);
@@ -538,6 +564,7 @@
element.addEventListener("wrn:error", onError);
flowCleanups.set(element, () => {
disposed = true;
window.clearTimeout(expiryTimer);
form?.removeEventListener("change", onMethodChange, true);
form?.removeEventListener("submit", onSubmit, true);
element.removeEventListener("wrn:success", onSuccess);