use super::{RpcHandler, DEV_DEFAULT_PASSWORD}; use anyhow::Result; impl RpcHandler { pub(super) async fn handle_auth_login( &self, params: Option, ) -> Result { let params = params.ok_or_else(|| anyhow::anyhow!("Missing params"))?; let password = params .get("password") .and_then(|v| v.as_str()) .ok_or_else(|| anyhow::anyhow!("Missing password"))?; let is_setup = self.auth_manager.is_setup().await?; if !is_setup { // Dev mode: allow default password so UI can log in without running setup if self.config.dev_mode && password == DEV_DEFAULT_PASSWORD { tracing::info!("[onboarding] login via dev default password"); return Ok(serde_json::Value::Null); } tracing::warn!("[onboarding] login attempt before setup complete"); return Err(anyhow::anyhow!( "User not set up. Please complete setup first." )); } let valid = self.auth_manager.verify_password(password).await?; if !valid { tracing::warn!("[onboarding] login failed — wrong password"); return Err(anyhow::anyhow!("Password Incorrect")); } tracing::info!("[onboarding] login successful"); Ok(serde_json::Value::Null) } pub(super) async fn handle_auth_logout(&self) -> Result { tracing::info!("[onboarding] logout"); Ok(serde_json::Value::Null) } pub(super) async fn handle_auth_change_password( &self, params: Option, session_token: &Option, ) -> Result { let params = params.ok_or_else(|| anyhow::anyhow!("Missing params"))?; let current_password = params .get("currentPassword") .and_then(|v| v.as_str()) .ok_or_else(|| anyhow::anyhow!("Missing currentPassword"))?; let new_password = params .get("newPassword") .and_then(|v| v.as_str()) .ok_or_else(|| anyhow::anyhow!("Missing newPassword"))?; let also_change_ssh = params .get("alsoChangeSsh") .and_then(|v| v.as_bool()) .unwrap_or(true); self.auth_manager .change_password(current_password, new_password, also_change_ssh) .await?; // Session rotation: invalidate all other sessions, rotate the caller's session if let Some(token) = session_token { self.session_store.invalidate_all_except(token).await; } Ok(serde_json::json!({ "success": true, "session_rotated": true })) } pub(super) async fn handle_auth_is_setup(&self) -> Result { let is_setup = self.auth_manager.is_setup().await?; Ok(serde_json::json!(is_setup)) } pub(super) async fn handle_auth_setup( &self, params: Option, ) -> Result { // Prevent re-setup if already set up let is_setup = self.auth_manager.is_setup().await?; if is_setup { tracing::warn!("[onboarding] setup rejected — already set up"); return Err(anyhow::anyhow!("Already set up. Use auth.changePassword to change.")); } let params = params.ok_or_else(|| anyhow::anyhow!("Missing params"))?; let password = params .get("password") .and_then(|v| v.as_str()) .ok_or_else(|| anyhow::anyhow!("Missing password"))?; if password.len() < 8 { tracing::warn!("[onboarding] setup rejected — password too short"); return Err(anyhow::anyhow!("Password must be at least 8 characters")); } self.auth_manager.setup_user(password).await?; tracing::info!("[onboarding] user setup complete"); Ok(serde_json::json!(true)) } pub(super) async fn handle_auth_onboarding_complete(&self) -> Result { self.auth_manager.complete_onboarding().await?; tracing::info!("[onboarding] onboarding marked complete"); Ok(serde_json::json!(true)) } pub(super) async fn handle_auth_is_onboarding_complete(&self) -> Result { let complete = self.auth_manager.is_onboarding_complete().await?; tracing::debug!("[onboarding] isOnboardingComplete={}", complete); Ok(serde_json::json!(complete)) } pub(super) async fn handle_auth_reset_onboarding( &self, params: Option, ) -> Result { let params = params.ok_or_else(|| anyhow::anyhow!("Missing params"))?; let password = params .get("password") .and_then(|v| v.as_str()) .ok_or_else(|| anyhow::anyhow!("Missing password — re-authentication required"))?; let valid = self.auth_manager.verify_password(password).await?; if !valid { tracing::warn!("[onboarding] reset rejected — wrong password"); return Err(anyhow::anyhow!("Password Incorrect")); } self.auth_manager.reset_onboarding().await?; tracing::info!("[onboarding] onboarding reset"); Ok(serde_json::json!(true)) } }