From df1c5f5c2ae8c9f8e9d72b1b362fea89fb5a9a26 Mon Sep 17 00:00:00 2001 From: Ruben Rosario Date: Sat, 20 Jun 2026 20:30:26 +0100 Subject: [PATCH] fix: include client_secret in device code request and improve error guidance - Include client_secret in the initial POST to /device/code endpoint - Add check for empty GOOGLE_CLIENT_SECRET with clear error message - Improve 'Invalid client type' error to suggest creating Desktop app OAuth client - Include error code in addition to description for better diagnostics --- src/infrastructure/api.rs | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/src/infrastructure/api.rs b/src/infrastructure/api.rs index 720172d..298400b 100644 --- a/src/infrastructure/api.rs +++ b/src/infrastructure/api.rs @@ -72,15 +72,25 @@ impl ApiClient { pub async fn authenticate(&self) -> Result<(String, String), ApiError> { if self.client_id.is_empty() { return Err(ApiError::Auth( - "GOOGLE_CLIENT_ID not set".to_string(), + "GOOGLE_CLIENT_ID not set. Set both GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET".to_string(), + )); + } + if self.client_secret.is_empty() { + return Err(ApiError::Auth( + "GOOGLE_CLIENT_SECRET not set. Set both GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET".to_string(), )); } - let params = serde_json::json!({ + let mut params = serde_json::json!({ "client_id": self.client_id, "scope": "https://www.googleapis.com/auth/tasks", }); + // Some client types require client_secret in the initial request + if !self.client_secret.is_empty() { + params["client_secret"] = serde_json::Value::String(self.client_secret.clone()); + } + let resp = self .client .post("https://oauth2.googleapis.com/device/code") @@ -96,11 +106,22 @@ impl ApiClient { .map_err(|e| ApiError::Api(format!("Invalid response (status {}): {}", status, e)))?; if !status.is_success() { + let err_code = data["error"].as_str().unwrap_or("unknown_error"); let err_desc = data["error_description"] .as_str() - .or_else(|| data["error"].as_str()) - .unwrap_or("unknown error"); - return Err(ApiError::Api(format!("OAuth error ({}): {}", status, err_desc))); + .unwrap_or("no description"); + + let mut msg = format!("OAuth error ({}): {} - {}", status, err_code, err_desc); + + // Common fixes for known errors + if err_desc.contains("Invalid client") || err_code == "invalid_client" { + msg.push_str( + ". Check that you created a 'Desktop app' OAuth 2.0 Client ID in Google Cloud Console \ + (not 'Web application'). Also verify that Google Tasks API is enabled.", + ); + } + + return Err(ApiError::Api(msg)); } let url = data["verification_url"]