Files
Netcatty/domain/sshAuth.ts
T
bincxz 5918f91132 Improves 2FA and SSH authentication handling
Enhances keyboard-interactive (2FA/MFA) authentication by implementing a queue-based system, allowing multiple concurrent requests to be processed sequentially.

Previously, password prompts during keyboard-interactive authentication were auto-filled if a saved password was available. This change removes the auto-fill behavior to prevent issues with custom or ambiguous prompt texts, instead providing a user-initiated "Use saved password" option in the UI.

Increases the connection timeout to 120 seconds to provide ample time for users to complete 2FA challenges. A new UI indicator shows when additional 2FA requests are pending.

Also, refines SSH authentication logic to strictly respect explicit password authentication, preventing unintended attempts to use private keys when password authentication is selected.
2026-01-20 17:59:42 +08:00

91 lines
2.2 KiB
TypeScript

import type { Host, Identity, SSHKey } from "./models";
export type HostAuthMethod = "password" | "key" | "certificate";
export type HostAuthOverride = {
authMethod?: HostAuthMethod;
username?: string;
password?: string;
keyId?: string;
passphrase?: string;
};
export type ResolvedHostAuth = {
identity?: Identity;
authMethod: HostAuthMethod;
username: string;
password?: string;
keyId?: string;
key?: SSHKey;
passphrase?: string;
};
const inferAuthMethod = (opts: {
explicit?: HostAuthMethod;
keyId?: string;
password?: string;
hostAuthMethod?: HostAuthMethod;
key?: SSHKey;
}): HostAuthMethod => {
if (opts.explicit) return opts.explicit;
if (opts.keyId) {
if (opts.hostAuthMethod === "key" || opts.hostAuthMethod === "certificate") {
return opts.hostAuthMethod;
}
return opts.key?.certificate ? "certificate" : "key";
}
if (opts.hostAuthMethod) return opts.hostAuthMethod;
if (opts.password) return "password";
return "password";
};
export const resolveHostAuth = (args: {
host: Host;
keys: SSHKey[];
identities?: Identity[];
override?: HostAuthOverride | null;
}): ResolvedHostAuth => {
const { host, keys, identities = [], override } = args;
const identity = host.identityId
? identities.find((i) => i.id === host.identityId)
: undefined;
const username =
override?.username?.trim() ||
identity?.username?.trim() ||
host.username?.trim() ||
"";
// Don't load key when explicit password auth is requested
// This ensures user's auth method selection is strictly respected
const keyId = override?.authMethod === 'password'
? undefined
: (override?.keyId || identity?.keyId || host.identityFileId || undefined);
const key = keyId ? keys.find((k) => k.id === keyId) : undefined;
const password = override?.password ?? identity?.password ?? host.password;
const authMethod = inferAuthMethod({
explicit: override?.authMethod,
hostAuthMethod: (identity?.authMethod || host.authMethod) as HostAuthMethod | undefined,
keyId,
password,
key,
});
const passphrase = override?.passphrase || key?.passphrase || undefined;
return {
identity,
authMethod,
username,
password,
keyId,
key,
passphrase,
};
};