Skip to content

feat(ssh): support keyboard-interactive authentication (MFA)#8750

Open
jhkim0911 wants to merge 2 commits into
stablyai:mainfrom
jhkim0911:ssh-keyboard-interactive-auth
Open

feat(ssh): support keyboard-interactive authentication (MFA)#8750
jhkim0911 wants to merge 2 commits into
stablyai:mainfrom
jhkim0911:ssh-keyboard-interactive-auth

Conversation

@jhkim0911

Copy link
Copy Markdown

Problem

Connecting to a host that requires password auth followed by a keyboard-interactive MFA challenge (e.g. a university HPC cluster behind Duo push/OTP) fails immediately after the password is entered:

All configured authentication methods failed

The MFA prompt is never shown. The same host works with OpenSSH and VS Code Remote SSH. Fixes #8622.

Root cause

These servers accept the password as a partial success and then require a keyboard-interactive round (RFC 4256). ssh2 only attempts keyboard-interactive when tryKeyboard is set and a keyboard-interactive listener answers the prompts — Orca configured neither, so after the partial success no auth methods remained.

Changes

  • Set tryKeyboard: true on ssh2 connect configs and answer keyboard-interactive rounds through the existing credential request flow (ssh:credential-requestSshPassphraseDialog).
  • Password-looking prompts (masked, mention "password", not one-time/OTP) reuse the password credential kind and its in-memory cache, so servers that collect the login password via keyboard-interactive keep silent reconnects; a rejected cached password re-prompts instead of looping. Everything else (push approvals, passcodes, option menus) is forwarded to the user with the server's instructions, and the RFC 4256 echo flag controls input masking.
  • Clear the 30s ready timeout once the server starts prompting — it budgets the network handshake, not a human approving a push. The server's LoginGraceTime and the 120s credential dialog timeout still bound the wait.
  • A cancelled prompt fails the round with no answers and skips the password re-prompt fallback.

Testing

  • New unit tests for prompt classification/answering (ssh-keyboard-interactive.test.ts) and integration tests for the full password → MFA flow, cached-password auto-answer, and cancel behavior (ssh-connection.test.ts).
  • Verified end-to-end against a local ssh2 Server requiring password partial success + keyboard-interactive: the real SshConnection completes the full flow and connects.
  • pnpm typecheck, pnpm lint (incl. localization catalog/coverage and max-lines gates), and the SSH/IPC/renderer vitest suites all pass.

Servers that require a keyboard-interactive round (RFC 4256) after
password auth — typical for HPC clusters and other hosts behind
Duo/OTP/push MFA — failed with "All configured authentication methods
failed" right after the password was entered, because ssh2 never
advertised the keyboard-interactive method and had no handler for its
prompts. The same hosts connect fine with OpenSSH and VS Code Remote
SSH.

- Enable tryKeyboard on every ssh2 connect config and answer
  keyboard-interactive prompt rounds through the existing credential
  request flow, so the MFA challenge reaches the user instead of
  failing the connection.
- Route password-looking prompts (masked, mention "password", not
  one-time/OTP) through the password credential kind and its in-memory
  cache: servers that collect the login password via
  keyboard-interactive keep silent reconnects, and a rejected cached
  password falls through to a fresh prompt instead of looping. All
  other prompts (push approval, passcodes, option menus) always reach
  the user, with the server's instructions shown and the RFC 4256 echo
  flag deciding whether the input is masked.
- Stop the 30s ready timeout once the server starts prompting: it
  budgets the network handshake, not a human answering an MFA push.
  The server's LoginGraceTime and the 120s credential dialog timeout
  still bound the wait.
- Treat a cancelled keyboard-interactive prompt as an explicit user
  decision: fail the round with no answers and skip the password
  re-prompt fallback.

Fixes stablyai#8622
@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

SSH keyboard-interactive authentication was added with support for MFA prompts, password caching, cancellation, and echo-aware responses. Credential metadata now flows through SSH callbacks, IPC, preload types, and the renderer. The SSH connection handles keyboard-interactive events and prevents cancelled prompts from falling back to password authentication. The passphrase dialog and translations now support verification prompts and visible or hidden responses. Tests cover prompt handling, caching, cancellation, connection behavior, and configuration.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description covers the problem, changes, and testing, but it omits required template sections like Summary, Screenshots, AI Review Report, Security Audit, and Notes. Reformat the PR description to match the template and add the missing sections, especially screenshots for the UI change plus AI review, security audit, and notes.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes appear to satisfy #8622 by enabling keyboard-interactive SSH auth, routing prompts through the credential flow, and handling cancellation.
Out of Scope Changes check ✅ Passed The added tests, locale strings, and SSH/renderer plumbing all support the keyboard-interactive MFA feature and do not appear unrelated.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed The title clearly summarizes the main change: SSH keyboard-interactive authentication support for MFA.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
src/main/ssh/ssh-connection.ts (3)

1015-1028: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Rejection branch swallows the actual error.

The second .then() callback discards whatever collectKeyboardInteractiveResponses rejected with and always does finish([]). This means a genuine bug (e.g. an IPC failure inside requestCredential) is indistinguishable from an expected user cancellation, and there's no log to help diagnose it — the connection just fails with the generic ssh2 auth error.

♻️ Log the rejection reason
-    () => {
+    (err) => {
+      console.warn(
+        `[ssh] keyboard-interactive handling failed for ${this.target.label}: ${err instanceof Error ? err.message : String(err)}`
+      )
       if (!settled) {
         finish([])
       }
     }

978-1028: 🧹 Nitpick | 🔵 Trivial

Design note: no overall timeout bounds a human MFA response once the ready-timeout is cleared.

Per the "Why" comment, the wait is now bounded only by the server's LoginGraceTime and the credential dialog's own timeout. That's a reasonable tradeoff for MFA push/OTP, but if a host has LoginGraceTime 0 (disabled) and the user leaves the credential dialog open indefinitely, the SshClient/socket for that attempt stays alive until the user acts. Worth keeping in mind operationally (e.g. if support ever sees lingering SSH sockets), though not something to block this PR on given the documented rationale.


993-997: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Guard the ssh2 ready-timeout handle This reaches into ssh2’s internal _readyTimeout field; if a future update changes it, the clear becomes a silent no-op and MFA prompts can fall back to the handshake timeout again. Add a guard/log here so that breakage is visible.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: f20f9728-183a-491b-ba42-dafeaad3dd60

📥 Commits

Reviewing files that changed from the base of the PR and between e0edc8e and d7b3f45.

📒 Files selected for processing (17)
  • src/main/ipc/ssh-passphrase.ts
  • src/main/ipc/ssh.ts
  • src/main/ssh/ssh-connection-utils.test.ts
  • src/main/ssh/ssh-connection-utils.ts
  • src/main/ssh/ssh-connection.test.ts
  • src/main/ssh/ssh-connection.ts
  • src/main/ssh/ssh-keyboard-interactive.test.ts
  • src/main/ssh/ssh-keyboard-interactive.ts
  • src/preload/api-types.ts
  • src/preload/index.ts
  • src/renderer/src/components/settings/SshPassphraseDialog.tsx
  • src/renderer/src/i18n/locales/en.json
  • src/renderer/src/i18n/locales/es.json
  • src/renderer/src/i18n/locales/ja.json
  • src/renderer/src/i18n/locales/ko.json
  • src/renderer/src/i18n/locales/zh.json
  • src/renderer/src/store/slices/ssh.ts

… them

Review follow-up for keyboard-interactive support:

- Log the rejection reason when answering a keyboard-interactive round
  fails, so a broken credential prompter is distinguishable from an
  expected user cancellation.
- Guard the ssh2 internal _readyTimeout access and warn when the handle
  is missing, so a future ssh2 internals change can't silently revert
  MFA prompts to the 30s handshake timeout.
@jhkim0911

Copy link
Copy Markdown
Author

Addressed the review feedback in 87e7a59:

  • The keyboard-interactive rejection handler now logs the failure reason, so a broken credential prompter is distinguishable from an expected user cancellation.
  • The ssh2 internal _readyTimeout access is now guarded: if the field ever moves in a future ssh2 release, we warn instead of silently falling back to the 30s handshake timeout mid-MFA.

On the unbounded-wait design note: agreed it's worth keeping in mind. In practice the credential dialog resolves null after 120s, which fails the round with no answers, so the client isn't waiting forever on the dialog itself — the truly unbounded case needs LoginGraceTime 0 on the server plus a dismissed-but-never-answered prompt, which seemed acceptable to leave as documented behavior.

@jhkim0911 jhkim0911 marked this pull request as draft July 15, 2026 03:39
@jhkim0911 jhkim0911 marked this pull request as ready for review July 15, 2026 03:39
@AmethystLiang AmethystLiang requested a review from Jinwoo-H July 15, 2026 16:48
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.

[Bug]: Support SSH keyboard-interactive authentication (push-based MFA)

2 participants