Add username/password login with keyring-backed credential storage

- Dual-mode auth dialog: username/password (default) + API token fallback
- Proxmox ticket-based auth via POST /access/ticket
- OS keyring integration for secure credential persistence
- Auto-open login dialog on launch when unauthenticated
- Auth state tracking with auto-refresh support
This commit is contained in:
Matt
2026-07-29 18:27:27 +00:00
parent 084b64872e
commit dbe3c8d2a1
8 changed files with 477 additions and 152 deletions
+53
View File
@@ -27,6 +27,8 @@ pub struct ConnectionConfig {
pub status: String,
pub cluster_name: Option<String>,
pub is_cluster: bool,
pub auth_mode: String,
pub username: Option<String>,
}
#[derive(Clone, Serialize, Deserialize)]
@@ -36,6 +38,13 @@ pub struct EndpointConfig {
pub token: Option<String>,
}
#[derive(Clone, Serialize, Deserialize)]
pub struct LoginResult {
pub connection_id: String,
pub ticket: String,
pub csrf_token: String,
}
#[derive(Clone, Serialize, Deserialize)]
pub struct CertificateInfo {
pub fingerprint: String,
@@ -407,6 +416,46 @@ async fn migrate_vm(
manager.migrate_vm(&connection_id, &node, vmid, &target_node, online).await
}
// Authentication commands
#[tauri::command]
async fn login_with_password(
state: tauri::State<'_, AppState>,
url: String,
username: String,
password: String,
) -> Result<LoginResult> {
let manager = state.connection_manager.read().await;
manager.login_with_password(&url, &username, &password).await
}
#[tauri::command]
async fn login_with_token(
state: tauri::State<'_, AppState>,
url: String,
token: String,
) -> Result<LoginResult> {
let manager = state.connection_manager.read().await;
manager.login_with_token(&url, &token).await
}
#[tauri::command]
async fn logout(
state: tauri::State<'_, AppState>,
connection_id: String,
) -> Result<()> {
let manager = state.connection_manager.read().await;
manager.logout(&connection_id).await
}
#[tauri::command]
async fn get_stored_credentials(
state: tauri::State<'_, AppState>,
connection_id: String,
) -> Result<Option<String>> {
let manager = state.connection_manager.read().await;
manager.get_stored_credentials(&connection_id).await
}
// Console proxy types
#[derive(Clone, Serialize, Deserialize)]
pub struct VNCProxyResponse {
@@ -637,6 +686,10 @@ pub fn run() {
remove_connection,
connect_to_server,
disconnect_from_server,
login_with_password,
login_with_token,
logout,
get_stored_credentials,
get_certificate_info,
trust_certificate,
get_nodes,