/** * Passphrase Modal * Modal for requesting passphrase for encrypted SSH keys */ import { Eye, EyeOff, KeyRound, Loader2 } from "lucide-react"; import React, { useCallback, useEffect, useState } from "react"; import { useI18n } from "../application/i18n/I18nProvider"; import { Button } from "./ui/button"; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, } from "./ui/dialog"; import { Input } from "./ui/input"; import { Label } from "./ui/label"; export interface PassphraseRequest { requestId: string; keyPath: string; keyName: string; hostname?: string; sessionId?: string; bootEpoch?: number; } /** Opt-in only: do not default to remembering key passphrases (#2024). */ export const DEFAULT_REMEMBER_PASSPHRASE = false; interface PassphraseModalProps { request: PassphraseRequest | null; onSubmit: (requestId: string, passphrase: string, remember: boolean) => void; onCancel: (requestId: string) => void; onSkip?: (requestId: string) => void; } export const PassphraseModal: React.FC = ({ request, onSubmit, onCancel, onSkip, }) => { const { t } = useI18n(); const [passphrase, setPassphrase] = useState(""); const [showPassphrase, setShowPassphrase] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false); const [rememberPassphrase, setRememberPassphrase] = useState(DEFAULT_REMEMBER_PASSPHRASE); // Reset state when request changes useEffect(() => { if (request) { setPassphrase(""); setShowPassphrase(false); setIsSubmitting(false); setRememberPassphrase(DEFAULT_REMEMBER_PASSPHRASE); } }, [request]); const handleSubmit = useCallback(() => { if (!request || isSubmitting || !passphrase) return; setIsSubmitting(true); onSubmit(request.requestId, passphrase, rememberPassphrase); }, [request, passphrase, onSubmit, isSubmitting, rememberPassphrase]); const handleCancel = useCallback(() => { if (!request) return; onCancel(request.requestId); }, [request, onCancel]); const handleSkip = useCallback(() => { if (!request || !onSkip) return; onSkip(request.requestId); }, [request, onSkip]); const handleKeyDown = useCallback( (e: React.KeyboardEvent) => { if (e.key === "Enter" && !isSubmitting && passphrase) { e.preventDefault(); handleSubmit(); } }, [handleSubmit, isSubmitting, passphrase] ); if (!request) return null; const keyDisplayName = request.keyName || request.keyPath.split("/").pop() || "SSH Key"; return ( !open && handleCancel()}>
{t("passphrase.title")} {request.hostname ? t("passphrase.descWithHost", { keyName: keyDisplayName, hostname: request.hostname }) : t("passphrase.desc", { keyName: keyDisplayName })}
setPassphrase(e.target.value)} onKeyDown={handleKeyDown} placeholder="" className="pr-10" autoFocus disabled={isSubmitting} />

{t("passphrase.keyPath")}: {request.keyPath}

{onSkip && ( )}
); }; export default PassphraseModal;