feat(ssh): pre-populate vault password in auth prompts for MFA (fixes #9911) (#11170)

* feat(ssh): pre-populate vault password in auth prompts for MFA use cases

When a keyboard-interactive password prompt is shown and the response
field is empty, load and pre-fill the saved vault password. This lets
users with hardware tokens (e.g. YubiKey) press their token to append
a TOTP to the pre-filled base password without re-typing it.

Also pre-fill the prompt-password modal with the vault password so the
same workflow applies when SSH password auth is used with MFA.

Closes #9911

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ssh): remove unnecessary null coalescing in loadPassword call

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
syrexjohnson
2026-04-21 22:15:19 +02:00
committed by GitHub
parent 1e262cb585
commit 02216ff4e7
2 changed files with 22 additions and 3 deletions
@@ -1,4 +1,4 @@
import { Component, Input, Output, EventEmitter, ViewChild, ElementRef, ChangeDetectionStrategy } from '@angular/core'
import { Component, Input, Output, EventEmitter, ViewChild, ElementRef, ChangeDetectionStrategy, OnInit, ChangeDetectorRef } from '@angular/core'
import { KeyboardInteractivePrompt } from '../session/ssh'
import { SSHProfile } from '../api'
import { PasswordStorageService } from '../services/passwordStorage.service'
@@ -9,7 +9,7 @@ import { PasswordStorageService } from '../services/passwordStorage.service'
styleUrls: ['./keyboardInteractiveAuthPanel.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class KeyboardInteractiveAuthComponent {
export class KeyboardInteractiveAuthComponent implements OnInit {
@Input() profile: SSHProfile
@Input() prompt: KeyboardInteractivePrompt
@Input() step = 0
@@ -17,7 +17,22 @@ export class KeyboardInteractiveAuthComponent {
@ViewChild('input') input: ElementRef
remember = false
constructor (private passwordStorage: PasswordStorageService) {}
constructor (
private passwordStorage: PasswordStorageService,
private cdr: ChangeDetectorRef,
) {}
async ngOnInit (): Promise<void> {
const savedPassword = await this.passwordStorage.loadPassword(this.profile)
if (savedPassword) {
for (let i = 0; i < this.prompt.prompts.length; i++) {
if (this.prompt.isAPasswordPrompt(i) && !this.prompt.responses[i]) {
this.prompt.responses[i] = savedPassword
}
}
this.cdr.markForCheck()
}
}
isPassword (): boolean {
return this.prompt.isAPasswordPrompt(this.step)
+4
View File
@@ -657,6 +657,10 @@ export class SSHSession {
modal.componentInstance.prompt = `Password for ${this.authUsername}@${this.profile.options.host}`
modal.componentInstance.password = true
modal.componentInstance.showRememberCheckbox = true
const prefilledPassword = await this.passwordStorage.loadPassword(this.profile, this.authUsername)
if (prefilledPassword) {
modal.componentInstance.value = prefilledPassword
}
try {
const promptResult = await modal.result.catch(() => null)