diff --git a/dropbox_auth_manager.py b/dropbox_auth_manager.py index 1635e33..ca1666c 100644 --- a/dropbox_auth_manager.py +++ b/dropbox_auth_manager.py @@ -20,17 +20,26 @@ def setup_keyring_backend(): print(f"[DropboxAuthManager] Skipping interactive keyring ({keyring_name}) to avoid blocking") raise Exception("Interactive keyring detected - using fallback") - # Test non-interactively - test_service = "test_keyring_availability" - keyring.set_password(test_service, "test", "test") - keyring.delete_password(test_service, "test") + # Test non-interactively with our actual service name to be more thorough + test_service = DROPBOX_KEYRING_SERVICE + test_key = "test_key_interactive_check" + test_value = "test_value" + + print(f"[DropboxAuthManager] Testing keyring ({keyring_name}) for interactive prompts...") + keyring.set_password(test_service, test_key, test_value) + retrieved = keyring.get_password(test_service, test_key) + keyring.delete_password(test_service, test_key) + + if retrieved != test_value: + raise Exception("Keyring test failed - retrieved value doesn't match") + print(f"[DropboxAuthManager] Using system keyring backend: {keyring_name}") return True except Exception as e: print(f"[DropboxAuthManager] System keyring not available or interactive: {e}") - # Try keyrings.alt file-based backends + # Force fallback to plaintext file keyring to avoid any interactive prompts try: import keyrings.alt.file @@ -44,7 +53,7 @@ def setup_keyring_backend(): keyring.set_keyring(file_keyring) - # Test it works + # Test the file keyring works test_service = "test_file_keyring" keyring.set_password(test_service, "test", "test") keyring.delete_password(test_service, "test") @@ -60,10 +69,6 @@ def setup_keyring_backend(): except Exception as file_error: print(f"[DropboxAuthManager] File keyring failed: {file_error}") return False - - except Exception as e: - print(f"[DropboxAuthManager] All keyring backends failed: {e}") - return False class DropboxAuthManager: def __init__(self, app_key=None, app_secret=None): @@ -120,10 +125,12 @@ class DropboxAuthManager: return bool(self.app_key and self.app_secret and self.refresh_token) def store_tokens(self, app_key, app_secret, refresh_token): - if not self._ensure_keyring_setup(): - raise RuntimeError("Keyring not available. Please use 'env_file' or 'display_only' storage method instead.") - + """Store tokens in keyring with fallback for interactive keyrings""" try: + if not self._ensure_keyring_setup(): + raise RuntimeError("Keyring not available. Please use 'env_file' or 'display_only' storage method instead.") + + print(f"[DropboxAuthManager] Storing tokens using keyring...") keyring.set_password(DROPBOX_KEYRING_SERVICE, "app_key", app_key) keyring.set_password(DROPBOX_KEYRING_SERVICE, "app_secret", app_secret) keyring.set_password(DROPBOX_KEYRING_SERVICE, "refresh_token", refresh_token) @@ -132,8 +139,15 @@ class DropboxAuthManager: self.app_key = app_key self.app_secret = app_secret self.refresh_token = refresh_token + print(f"[DropboxAuthManager] Tokens stored successfully in keyring") + except KeyringError as e: raise RuntimeError(f"Failed to store Dropbox tokens securely: {e}") + except Exception as e: + # Handle cases where keyring setup succeeded but storage fails (e.g., interactive prompts) + error_msg = f"Keyring storage failed - this may be due to interactive keyring prompts: {e}" + print(f"[DropboxAuthManager] {error_msg}") + raise RuntimeError(error_msg) def exchange_auth_code(self, auth_code, redirect_uri=None): if not (self.app_key and self.app_secret): diff --git a/oauth_handler.py b/oauth_handler.py index f5e755d..0242c12 100644 --- a/oauth_handler.py +++ b/oauth_handler.py @@ -46,19 +46,29 @@ class OAuthCallbackHandler: print(f"[OAuthHandler] Redirect URI: {redirect_uri}") # Use DropboxAuthManager to exchange the code + print(f"[OAuthHandler] Creating DropboxAuthManager for token exchange...") auth_manager = DropboxAuthManager(self.app_key, self.app_secret) + print(f"[OAuthHandler] Exchanging auth code for tokens...") result = auth_manager.exchange_auth_code_raw(auth_code, redirect_uri=redirect_uri) refresh_token = result.get("refresh_token") - print(f"[OAuthHandler] Auth code exchange successful") + print(f"[OAuthHandler] Auth code exchange successful - got refresh token: {bool(refresh_token)}") print(f"[OAuthHandler] Using storage method: {self.storage_method}") # Handle different storage methods success_message = None if self.storage_method == "keyring": # Store in system keyring (original behavior) - auth_manager.store_tokens(self.app_key, self.app_secret, refresh_token) - success_message = "✅ Dropbox connected successfully! Credentials stored securely in system keyring." + print(f"[OAuthHandler] Attempting to store tokens in keyring...") + try: + auth_manager.store_tokens(self.app_key, self.app_secret, refresh_token) + print(f"[OAuthHandler] Tokens stored in keyring successfully") + success_message = "✅ Dropbox connected successfully! Credentials stored securely in system keyring." + except Exception as e: + print(f"[OAuthHandler] ERROR storing tokens in keyring: {e}") + # Fall back to display_only if keyring fails + success_message = f"⚠️ Keyring storage failed: {e}. Please use 'display_only' or 'env_file' storage method." + return False, success_message elif self.storage_method == "env_file": # Store in .env file