git.lucas.co / cce-mail
mail client (IMAP/SMTP)
git clone https://git.lucas.co/cce-mail.git

commitc7b35ea78a9828374697d72b35e017747a53729f
parent24fd0511cd
authorLucas Galante <[email protected]>
date2026-09-20 14:13
Google OAuth client comes only from google_client.json

A placeholder client ID and secret were compiled in and written to
google_client.json whenever it was missing: the same shape as the live
credential cce-system-interface shipped, one edit away from repeating it.
The file is now the only source, written by the System Interface's
Accounts page; missing means "not set up", and the token refresh says so
instead of sending empty strings to Google.

Co-Authored-By: Claude Fable 5.1 <[email protected]>

 src/main.rs | 38 ++++++++++++++------------------------
 1 file changed, 14 insertions(+), 24 deletions(-)

diff --git a/src/main.rs b/src/main.rs
index 7297172..7e0e8ed 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1334,8 +1334,6 @@ fn parse_mailto(arg: &str) -> Option<MailtoPrefill> {
     Some(prefill)
 }
 
-const GOOGLE_CLIENT_ID: &str = "946029775684-m4u4mme60a6a0qj3p5m5jvea8d2987o9.apps.googleusercontent.com";
-const GOOGLE_CLIENT_SECRET: &str = "GOCSPX-dummysecret";
 
 #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
 struct GoogleClientConfig {
@@ -1343,32 +1341,21 @@ struct GoogleClientConfig {
     client_secret: String,
 }
 
+/// The Google OAuth client, from google_client.json, which the System
+/// Interface's Accounts page writes when the user pastes their own client's
+/// ID and secret. There is no built-in default any more: a placeholder ID
+/// and secret used to be compiled in here and written to that file when it
+/// was missing, which is one hardcoded credential away from the mistake
+/// cce-system-interface made. Missing or unreadable means "not set up", and
+/// the token refresh says so instead of sending empty strings to Google.
 fn load_google_client_config() -> GoogleClientConfig {
     let p = cce_ui::config::cce_config_dir().join("google_client.json");
-    if p.exists() {
-        if let Ok(content) = std::fs::read_to_string(&p) {
-            if let Ok(config) = serde_json::from_str::<GoogleClientConfig>(&content) {
-                return config;
-            }
+    if let Ok(content) = std::fs::read_to_string(&p) {
+        if let Ok(config) = serde_json::from_str::<GoogleClientConfig>(&content) {
+            return config;
         }
     }
-    let default_config = GoogleClientConfig {
-        client_id: GOOGLE_CLIENT_ID.to_string(),
-        client_secret: GOOGLE_CLIENT_SECRET.to_string(),
-    };
-    if let Ok(content) = serde_json::to_string_pretty(&default_config) {
-        let _ = std::fs::write(&p, content);
-        #[cfg(unix)]
-        {
-            use std::os::unix::fs::PermissionsExt;
-            if let Ok(metadata) = std::fs::metadata(&p) {
-                let mut perms = metadata.permissions();
-                perms.set_mode(0o600);
-                let _ = std::fs::set_permissions(&p, perms);
-            }
-        }
-    }
-    default_config
+    GoogleClientConfig { client_id: String::new(), client_secret: String::new() }
 }
 
 async fn refresh_access_token(account: &mut AccountInfo) -> Result<String, String> {
@@ -1396,6 +1383,9 @@ async fn refresh_access_token(account: &mut AccountInfo) -> Result<String, Strin
     let client_config = load_google_client_config();
     let client_id = account.client_id.as_deref().unwrap_or(&client_config.client_id);
     let client_secret = account.client_secret.as_deref().unwrap_or(&client_config.client_secret);
+    if client_id.is_empty() || client_secret.is_empty() {
+        return Err("Google client not configured: set the client ID and secret in System Interface > Accounts".to_string());
+    }
 
     let client = reqwest::Client::new();
     let params = [