Skip to content

Handle 2FA device selector dialog on multi-method accounts (refs #20) - #21

Merged
code-hustler-ft3d merged 1 commit into
code-hustler-ft3d:mainfrom
xuanmingguo:fix/2fa-device-selector-multimethod
Aug 15, 2026
Merged

Handle 2FA device selector dialog on multi-method accounts (refs #20)#21
code-hustler-ft3d merged 1 commit into
code-hustler-ft3d:mainfrom
xuanmingguo:fix/2fa-device-selector-multimethod

Conversation

@xuanmingguo

Copy link
Copy Markdown

Closes #7 (partial): multi-method 2FA dialog is a device selector and is now detected + driven mechanically; the switch is rejected server-side on 10.45.1c (see #20), so on current versions the net effect is accurate failure detection and a SETTEXT fallback bugfix.

Changes

agent/GatewayInputAgent.java

  • New JLIST_SELECT <title>|<item> command (protocol v0.2 → v0.3): select an item by text in the first JList of a window.
  • SETTEXT_IN_WIN fallback: only consider JTextField/JPasswordField, never JTextArea headings — the old catch-all fallback typed the TOTP into the Select second factor device heading (silently, returning OK).

gateway_controller.py

  • agent_jlist_select() helper.
  • handle_2fa TOTP branch: detect the device selector via component-tree dump (the heading is a JTextArea, invisible to agent_labels()), select TWOFA_DEVICE, click OK, poll up to 15s for the code-entry dialog. If the switch is rejected → clear ALERT_2FA_FAILED (previously: 2FA handled successfully then a failed login).
  • SETTEXT/CLICK on the 2FA dialog try multiple title substrings since the code-entry dialog's title may differ from the selector's.

Test plan (hand-written, per CONTRIBUTING)

  1. make clean && make passes (JDK 17). ✅ verified locally.
  2. Single-method account: selector absent → flow unchanged (no regression).
  3. Multi-method 10.45.1c: selector detected → JLIST_SELECT OK → OK clicked → poll → no code dialog → ALERT_2FA_FAILED (previously silent wrong-typing). ✅ verified on a real account (3 attempts; see issue 2FA device selector on multi-method accounts: drivable in-JVM, but switch rejected server-side on 10.45.1c #20 for logs).
  4. SETTEXT_IN_WIN with only JTextArea headings → ERR (was: OK + text in heading).

Full findings, component-tree dump and screenshot description in issue #20.

…e-hustler-ft3d#7, refs code-hustler-ft3d#20)

The Second Factor Authentication dialog on a multi-method account is a
device selector (JTextArea heading 'Select second factor device' + a
JList of devices + OK/Cancel/Help), not a code-entry form. The
controller previously typed the TOTP into the heading JTextArea via
SETTEXT_IN_WIN's catch-all fallback and clicked OK — appearing to
succeed while submitting nothing, then failing with 'Re-login is
required'.

Changes:
- agent: add JLIST_SELECT command (protocol v0.2 -> v0.3) to select an
  item by text in the first JList of a window (mirrors IBC's
  SecondFactorDevice handling of the same dialog)
- agent: SETTEXT_IN_WIN fallback now only considers JTextField /
  JPasswordField, never JTextArea headings — prevents silently typing
  into dialog headings
- controller: detect the device selector, select TWOFA_DEVICE, click
  OK, poll for the code-entry dialog; fail with ALERT_2FA_FAILED if
  the switch is rejected instead of mis-typing
- controller: broaden SETTEXT/CLICK title candidates since the
  code-entry dialog title may differ from the selector's

Caveat: on Gateway 10.45.1c the server rejects the mid-login device
switch (Re-login is required, 3/3 reproducible; device choice does not
persist). The PR still delivers accurate failure detection and the
fallback bugfix; the switch itself may work on older Gateway versions.
See issue code-hustler-ft3d#20 for full findings and test plan.
@code-hustler-ft3d

Copy link
Copy Markdown
Owner

Thanks — this is a solid find, and the dump in #20 settles something left open since the v0.7.0 spike: the multi-method dialog shape is account-dependent. The account v0.7.0 was validated against gets a pre-defaulted code dialog with a hidden "Change input method" link (no JList anywhere in its tree); yours gets the IBC-style selector. Both are real on 10.45.x.

I reproduced your findings against a mock dialog built from your component-tree dump, driven by the real agent jar and the real handle_2fa:

I've pushed your commit untouched to fix/2fa-device-selector-integration with integration follow-ups on top:

  • Rejected-switch now fails with a dedicated reason at the pollALERT_2FA_FAILED reason="2FA device switch produced no code-entry dialog" plus a remediation line — instead of falling through to a misleading SETTEXT_IN_WIN failure.
  • The readiness poll watches for the "Enter code" prompt (via the existing _twofa_requested_method) rather than window titles: both the selector and the code dialog are titled "Second Factor Authentication", so a title match can't distinguish them. This also made the multi-title SETTEXT/CLICK retry lists unnecessary, restoring the documented ALERT strings.
  • Selector detection extracted to _twofa_selector_present + unit tests for both dialog variants (249 green), and CHANGELOG / OBSERVABILITY / ARCHITECTURE updated.

Could you run that branch against your multi-method account? Expected: within ~20s of the selector appearing you get the new reason="2FA device switch produced no code-entry dialog" line and a clean failure — no silent success. If you can, paste the controller log from 2FA dialog detected: onward (dumps/log lines are redacted since v0.6.3, but do glance over them before posting).

That rejected-switch path on a real account is the one thing that can't be exercised here — everything else (selector detection, list drive, link-variant no-regression, and the switch-accepted flow against a mock that swaps in the code panel) is covered. If your run behaves, I'll merge the PR with the follow-ups and cut a release.

@code-hustler-ft3d
code-hustler-ft3d merged commit db82506 into code-hustler-ft3d:main Aug 15, 2026
4 checks passed
@code-hustler-ft3d

Copy link
Copy Markdown
Owner

Merged to main — your commit landed as-is (db82506), with the integration follow-ups on top (b5a3980, bdc6f54). Since every outcome of the selector path is now fail-loud, there was no reason to hold the merge on a live run; consider the test request above superseded.

This ships in the next tagged release together with a docs pass (README/UPGRADING still describe only the code-dialog variant). No action needed on your side — though if you do get a chance to run main (or the next image) against your account, a log excerpt from 2FA dialog detected: onward posted to #20 would be welcome real-world confirmation of the new failure path.

Thanks for an excellent contribution — the component-tree dump in #20 was the ground truth this had been missing since May, and the SETTEXT_IN_WIN fallback catch fixed a bug that had been silently misreporting success on selector-shaped accounts.

code-hustler-ft3d added a commit that referenced this pull request Aug 15, 2026
The unittest invocation was piped through tail -5, so make test's
exit status was tail's and a failing suite still exited 0 — CI has
never been able to catch a unit-test regression. Drop the pipe (and
-v; non-verbose discover output is compact) and propagate failure.
Verified in both directions.

Add TestHandle2faSelectorFlow: switch-rejected emits the dedicated
ALERT reason and never types the code; switch-accepted completes the
selector -> code-entry -> TOTP flow; JLIST_SELECT failure fails loud;
a link-variant dump never touches the selector path. Mirrors the
mock-dialog harness scenarios that validated #21.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TWOFA_DEVICE ignored: multi-factor list defaults to IB Key first; TOTP mis-routed when Mobile Authenticator is second

3 participants