Fix SSH Key Password (keyPassword) Field Naming Mismatch Between Frontend and Backend #375
Reference in New Issue
Block a user
Delete Branch "fix--sshpasskey-missmatch"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Fix SSH Key Password (keyPassword) Field Naming Mismatch Between Frontend and Backend
🐛 Problem
The application was experiencing authentication failures when using encrypted SSH private keys with passwords. Users would see the error:
Additionally, the "Server Metrics" feature would fail to load with "Failed to fetch server metrics" error.
Root Cause
There was a field naming inconsistency between the frontend and backend:
keyPassword(camelCase)key_password(snake_case)key_password(snake_case)This mismatch caused the SSH key password to not be passed correctly to the SSH2 client, resulting in connection failures.
✅ Solution
Implemented a comprehensive field naming standardization across the entire codebase to use camelCase (
keyPassword) in the API layer while maintaining snake_case (key_password) in the database layer.📝 Changes Made
1. SSH Routes (
src/backend/database/routes/ssh.ts)/ssh/db/hostto acceptkeyPasswordfrom frontend/ssh/db/host/:idto acceptkeyPasswordfrom frontendresolveHostCredentials()to convertkey_password→keyPasswordwhen returning datakeyPasswordinstead ofkey_passwordkey_passwordfrom overwriting credential-resolvedkeyPasswordkeyPasswordinstead of deletedkey_passwordfield2. Server Stats (
src/backend/ssh/server-stats.ts)addLegacyCredentials()to readkey_passwordfrom database and convert tokeyPasswordhost.key_password || host.keyPassword3. Host Manager UI (
src/ui/Desktop/Apps/Host Manager/HostManagerEditor.tsx)authType3. Data Flow
🐛 Additional Bug Fix
Stale Data Override Prevention
Fixed a critical bug in the
resolveHostCredentials()function where a host using a credential ID could have its credential'skeyPasswordincorrectly overwritten by a stalekey_passwordproperty from the host object itself.Scenario:
key_password)key_passwordremained in the host recordkey_passwordwould override the credential'skeyPasswordSolution:
This ensures credential-resolved
keyPasswordalways takes precedence over stale inline values.🧪 Tested Scenarios
🔧 Technical Details
Field Conversion Strategy
keyPassword(camelCase) from frontendkey_password(snake_case) in database columnskey_password→keyPasswordbefore sending to frontendkeyPasswordas passphrase for SSH2 clientFiles Modified
src/backend/database/routes/ssh.tskey_passwordtokeyPassword(POST)keyPasswordinstead ofkey_passwordkey_passwordtokeyPassword(PUT)keyPasswordkeyPasswordkeyPasswordresolveHostCredentials()to prevent stale data overridesrc/backend/ssh/server-stats.tsaddLegacyCredentials()to convertkey_passwordtokeyPasswordBackward Compatibility
key_passwordandkeyPasswordduring reads (fallback logic)📊 Impact
🎯 Fixes
📸 Related Issues
Fixes: Authentication failures with encrypted SSH keys
Fixes: "Failed to fetch server metrics" error
Fixes: Stale inline credentials overriding credential-based auth
Breaking Changes: None
Rollback Plan: Revert commits in this PR if issues arise
Testing: Manual testing completed for all SSH connection scenarios including edge cases
Summary of Changes
Hello @thorved, I'm Gemini Code Assist1 ! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!
This pull request resolves a critical issue preventing the use of encrypted SSH keys with passwords and causing server metrics to fail. The core problem was a naming inconsistency for the SSH key password field between the frontend's camelCase expectation and the backend/database's snake_case usage. The solution standardizes the API contract to use 'keyPassword' for all interactions, implementing robust conversion logic to seamlessly translate between the API and database formats. This ensures that all SSH-related features now correctly handle encrypted keys, restoring full functionality and consistency.
Highlights
Using Gemini Code Assist
The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.
Invoking Gemini
You can request assistance from Gemini at any point by creating a comment using either
/gemini <command>or@gemini-code-assist <command>. Below is a summary of the supported commands on the current page./gemini review/gemini summary/gemini helpCustomization
To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a
.gemini/folder in the base of the repository. Detailed instructions can be found here.Limitations & Feedback
Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.
You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.
Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩︎
Code Review
This is a great pull request that addresses a critical bug with SSH key passwords. The description is very clear and detailed, which is much appreciated. The changes to standardize on
keyPasswordat the API layer while keepingkey_passwordat the DB layer are well-implemented across the board. I've found one potential issue in the credential resolution logic, which I've detailed in a specific comment. Otherwise, the changes look solid.@@ -1234,12 +1234,27 @@ async function resolveHostCredentials(host: any): Promise<any> {authType: credential.auth_type || credential.authType,There's a potential bug in this conversion logic. If a
hostobject uses acredentialIdbut also has a stalekey_passwordproperty from a previous configuration, this logic will incorrectly overwrite thekeyPasswordfrom the credential with the stalekey_passwordfrom the host object.This happens because the credential resolution logic merges credential properties into the
hostobject, but the originalhost.key_passwordis preserved. This block then unconditionally useshost.key_passwordif it exists.To fix this, we should only perform the conversion from
key_passwordtokeyPasswordifkeyPasswordhasn't already been set by the credential resolution logic.@@ -1234,12 +1234,27 @@ async function resolveHostCredentials(host: any): Promise<any> {authType: credential.auth_type || credential.authType,Key Password Precedence Fix
This document summarizes the small but critical fix that prevents stale inline
key_passwordvalues from overwriting credential-resolvedkeyPasswordvalues when resolving SSH host credentials.Why this matters
key_passwordremains on the host row, it must not overwrite thekeyPasswordsupplied by the referenced credential.The fix correctly:
keyPasswordis already set by credential resolution (line 1245 insrc/backend/database/routes/ssh.ts).host.key_passwordifkeyPasswordisundefined(line 1246).key_password) to maintain a clean, camelCase API contract (line 1249).Result: This prevents stale inline credentials from overwriting credential-resolved values and ensures proper precedence when building the API response.