View Issue Details
| ID | Project | Category | View Status | Date Submitted | Last Update |
|---|---|---|---|---|---|
| 0012034 | Taler | libeufin-bank webui | public | 2026-11-10 23:42 | 2026-11-10 23:42 |
| Reporter | bot-cg [agent] | Assigned To | Resolution deadline | ||
| Implementer | Reviewer | ||||
| Release signed by | Deployed by | ||||
| Verifier | Communicator | CM_7 | |||
| Data protection authority notification required | No | ||||
| CSIRT notification required | No | ||||
| Estimated effort (hours) | Actual effort (hours) | 0 | |||
| Fast-track | No | ||||
| Work package | none | ||||
| Priority | normal | Severity | minor | Reproducibility | have not tried |
| Status | new | Resolution | open | ||
| Summary | 0012034: Bank: multi-factor authentication dialog leaves focus on the button behind the overlay | ||||
| Description | What happens: Any MFA-protected action (sign in, send transfer, change password, delete account, confirm withdrawal, cashout) opens the 'Multi-factor authentication required' dialog (non-modal <dialog open>). SolveChallengeDialog passes `focus` to SolveMFAChallenges, but the prop was never used, so nothing in the dialog gets focus. Measured: after clicking 'Send transfer' activeElement stays on BUTTON[name=send] 'Send transfer' behind the grey overlay; after submitting the login form with Enter it stays in INPUT#password, and pressing Enter again re-submits the login instead of continuing. Returning from the code step with 'Back' leaves focus on BODY. Note: the TAN code step itself (`ref={doAutoFocus}` on SolveChallenge's input) does work in a rendered browser: the code input is focused after 'Send me a message', after 'I have a code', and after the automatic send for combi_and challenges. Nothing later in the render defeats it. Expected: Focus inside the dialog on the obvious next action, which is 'Send me a message' of the first challenge still to be done. When that code was already sent and the retransmission wait is running (the button is disabled, e.g. after 'Back'), focus 'I have a code' instead. Where: - packages/libeufin-bank-webui/src/pages/SolveMFA.tsx:484 - packages/libeufin-bank-webui/src/pages/SolveMFA.tsx:585 - packages/libeufin-bank-webui/src/pages/SolveMFA.tsx:599 Measured in headless Chromium (document.activeElement): before: BUTTON[name=send] 'Send transfer' (behind the overlay); login flow: INPUT#password behind the overlay; after 'Back': BODY after: BUTTON[name='send again'] 'Send me a message'; after 'Back': BUTTON 'I have a code'; Enter on it -> INPUT#tan-ch-1 (code) Proposed fix: SolveMFAChallenges now uses its `focus` prop: the first challenge row not yet done passes `focus` to its AsyncButtons (web-util Button `focus` prop, i.e. doAutoFocus on mount). 'Send me a message' always gets it for that row, and 'I have a code' gets it only while the code is already sent. Because 'Send me a message' does not depend on alreadySent, its ref does not change when the retransmission wait ends, so focus is not taken away from the user later. Patch against taler-typescript-core master attached; the same change is committed on branch dev/bots/autofocus (durian). Screenshots attached (focused element outlined in red): 'before' is master, 'after' is with the patch. Verified: tsc: no new errors (the pre-existing BankFrame.tsx/ActiveWithdrawal.test.tsx errors are unrelated). Unit tests 94/94 pass (ActiveWithdrawal.test.tsx excluded because @testing-library/preact is not installed). Chromium via puppeteer: send-transfer, login (keyboard only: type password, Enter, Enter, type code), 2 OR challenges, 2 AND challenges (auto-send -> code input), 'I have a code', and 'Back'. Extra shots: shots/bank--mfa-login--{before,after}.png, shots/bank--mfa-back-to-list--{before,after}.png, shots/bank--mfa-code-entry--{before,after}.png (the code input is focused in both). | ||||
| Tags | No tags attached. | ||||
| Attached Files | bank--mfa-challenge-list.diff (2,640 bytes)
diff --git a/packages/libeufin-bank-webui/src/pages/SolveMFA.tsx b/packages/libeufin-bank-webui/src/pages/SolveMFA.tsx
index b54f6a43d..4fcd390e6 100644
--- a/packages/libeufin-bank-webui/src/pages/SolveMFA.tsx
+++ b/packages/libeufin-bank-webui/src/pages/SolveMFA.tsx
@@ -478,6 +478,10 @@ function SolveMFAChallenges({
const hasSolvedEnough = currentChallenge.combi_and
? currentSolved.length === currentChallenge.challenges.length
: currentSolved.length > 0;
+ // The dialog opens while keyboard focus is still on the button that
+ // started the operation, behind the overlay, where Enter would submit it
+ // again. Move focus to the first challenge that still has to be done.
+ let focusPending = !!focus;
return (
<Fragment>
@@ -543,6 +547,8 @@ function SolveMFAChallenges({
solved.indexOf(challenge.challenge_id) !== -1 ||
(currentChallenge.combi_and &&
nextAndChallenge?.challenge_id !== challenge.challenge_id);
+ const focusHere = focusPending && !noNeedToComplete;
+ if (focusHere) focusPending = false;
return (
<div
@@ -576,15 +582,21 @@ function SolveMFAChallenges({
name="cancel"
class="text-sm font-semibold leading-6 text-onBackground"
disabled={noNeedToComplete}
+ focus={focusHere && alreadySent}
onClick={() => selectChallenge.run(challenge)}
>
<i18n.Translate>I have a code</i18n.Translate>
</AsyncButton>
+ {/* Not tied to alreadySent: focusing the disabled
+ button does nothing, while a ref that changes
+ when the retransmission wait ends would steal
+ focus later. */}
<AsyncButton
submit
name="send again"
class="disabled:opacity-50 disabled:cursor-default cursor-pointer rounded-md bg-primary px-3 py-2 text-sm font-semibold text-onPrimary shadow-sm hover:bg-primaryHover hover:text-white focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary"
+ focus={focusHere}
disabled={alreadySent || noNeedToComplete}
onClick={() => sendMessage.run(challenge)}
>
| ||||
| child of | 0012024 | new | Web UIs (bank, wallet, exchange AML/KYC, challenger, auditor, web-util forms): dialogs and single-purpose forms open without keyboard focus on the obvious field |
| Date Modified | Username | Field | Change |
|---|---|---|---|
| 2026-11-10 23:42 | bot-cg | New Issue | |
| 2026-11-10 23:42 | bot-cg | Relationship added | child of 0012024 |
| 2026-11-10 23:42 | bot-cg | File Added: bank--mfa-challenge-list.diff | |
| 2026-11-10 23:42 | bot-cg | File Added: bank--mfa-challenge-list--before.png | |
| 2026-11-10 23:42 | bot-cg | File Added: bank--mfa-challenge-list--after.png |