From 49f6f64e5cf5921e4ed93c028100e771b2a51c9a Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Wed, 20 May 2026 12:22:42 +1000 Subject: [PATCH] Add Autodesk callback port setting Persist the OAuth callback port in connector settings, expose it in the settings dialog, and use it when constructing the localhost callback URL. Generated with the assistance of an AI coding tool. --- src/bonsaiviewer-autodesk/README.md | 7 +++++-- .../bonsaiviewer_autodesk/connector.py | 4 ++-- .../bonsaiviewer_autodesk/settings.py | 20 +++++++++++++++++++ .../bonsaiviewer_autodesk/ui.py | 12 +++++++++++ 4 files changed, 39 insertions(+), 4 deletions(-) diff --git a/src/bonsaiviewer-autodesk/README.md b/src/bonsaiviewer-autodesk/README.md index f57475da56..bdd6ed51f1 100644 --- a/src/bonsaiviewer-autodesk/README.md +++ b/src/bonsaiviewer-autodesk/README.md @@ -28,7 +28,7 @@ bonsaiviewer-autodesk The connector launches without any configuration; on first run, invoke `open_settings` (or, equivalently, set the `APS_CLIENT_ID` env var) to -configure the Autodesk client id. +configure the Autodesk client id and OAuth callback port. Then send newline-delimited JSON-RPC 2.0 requests on `stdin`. Examples: @@ -51,6 +51,9 @@ The connector reads the Autodesk client id from two places, in order: overrides). 2. `/settings.json` (persisted via the settings dialog). +The OAuth callback host is always `localhost`. The callback port defaults to +`8080` and can be changed in the settings dialog. + The config directory is platform-specific: - Linux: `~/.config/bonsaiviewer-autodesk/` @@ -85,7 +88,7 @@ What is implemented: - Hub / project / folder browsing (Qt UI) - `pull_ifcfed_interactive`, `pull_ifcfed`, `pull_models`, `pull_models_interactive` - `push_ifcfed_interactive`, `push_ifcfed`, `push_model_interactive`, `push_model` -- `open_settings` — edit the client id, sign out +- `open_settings` — edit the client id and callback port, sign out - Connector-managed cache with sole-child invariant - Adjacent `.ifcfed.manifest` written/read alongside `.ifcfed` files diff --git a/src/bonsaiviewer-autodesk/bonsaiviewer_autodesk/connector.py b/src/bonsaiviewer-autodesk/bonsaiviewer_autodesk/connector.py index 0f2b721fa9..4181ea8d1f 100644 --- a/src/bonsaiviewer-autodesk/bonsaiviewer_autodesk/connector.py +++ b/src/bonsaiviewer-autodesk/bonsaiviewer_autodesk/connector.py @@ -35,7 +35,6 @@ def _upload_callback(report: Report) -> ApsProgress: CONNECTOR_ID = "autodesk" KEYRING_SERVICE = "bonsaiviewer-autodesk" -DEFAULT_CALLBACK_URL = "http://localhost:8080/" DEFAULT_SCOPE = "data:read data:write data:create" @@ -53,9 +52,10 @@ class AutodeskConnector: self.aps = None return token_store = KeyringTokenStore(service_name=KEYRING_SERVICE, username=client_id) + callback_url = f"http://localhost:{settings.stored_callback_port()}/" self.auth = AuthSessionService( client_id=client_id, - callback_url=DEFAULT_CALLBACK_URL, + callback_url=callback_url, scope=DEFAULT_SCOPE, token_store=token_store, ) diff --git a/src/bonsaiviewer-autodesk/bonsaiviewer_autodesk/settings.py b/src/bonsaiviewer-autodesk/bonsaiviewer_autodesk/settings.py index 3b4ebdf8d7..2e3b8b43f6 100644 --- a/src/bonsaiviewer-autodesk/bonsaiviewer_autodesk/settings.py +++ b/src/bonsaiviewer-autodesk/bonsaiviewer_autodesk/settings.py @@ -7,6 +7,9 @@ from pathlib import Path from typing import Any +DEFAULT_CALLBACK_PORT = 8080 + + def config_root() -> Path: system = platform.system() if system == "Windows": @@ -59,3 +62,20 @@ def save_client_id(client_id: str) -> None: data = _read() data["client_id"] = client_id.strip() _write(data) + + +def stored_callback_port() -> int: + value = _read().get("callback_port", DEFAULT_CALLBACK_PORT) + try: + port = int(value) + except (TypeError, ValueError): + return DEFAULT_CALLBACK_PORT + return port if 1 <= port <= 65535 else DEFAULT_CALLBACK_PORT + + +def save_callback_port(port: int) -> None: + if not 1 <= port <= 65535: + raise ValueError("Callback port must be between 1 and 65535.") + data = _read() + data["callback_port"] = port + _write(data) diff --git a/src/bonsaiviewer-autodesk/bonsaiviewer_autodesk/ui.py b/src/bonsaiviewer-autodesk/bonsaiviewer_autodesk/ui.py index 9138648ce4..7fa1a02586 100644 --- a/src/bonsaiviewer-autodesk/bonsaiviewer_autodesk/ui.py +++ b/src/bonsaiviewer-autodesk/bonsaiviewer_autodesk/ui.py @@ -550,6 +550,7 @@ class SettingsDialog(_BaseDialog): env_override = settings.env_client_id() stored = settings.stored_client_id() effective = env_override or stored + callback_port = settings.stored_callback_port() body = ctk.CTkFrame(self, fg_color="transparent") body.pack(fill="both", expand=True, padx=24, pady=24) @@ -583,6 +584,11 @@ class SettingsDialog(_BaseDialog): text_color=("#b45309", "#f59e0b"), ).pack(fill="x", pady=(0, 8)) + ctk.CTkLabel(body, text="OAuth callback port", anchor="w").pack(fill="x") + self.callback_port_entry = ctk.CTkEntry(body, placeholder_text=str(settings.DEFAULT_CALLBACK_PORT)) + self.callback_port_entry.pack(fill="x", pady=(6, 8)) + self.callback_port_entry.insert(0, str(callback_port)) + self.status_label = ctk.CTkLabel( body, text=f"Signed in as {effective}" if effective else "No client id configured.", @@ -618,6 +624,12 @@ class SettingsDialog(_BaseDialog): def _save(self) -> None: new_id = self.client_id_entry.get().strip() + try: + callback_port = int(self.callback_port_entry.get().strip()) + settings.save_callback_port(callback_port) + except ValueError: + show_error(title="Invalid Callback Port", message="Callback port must be a number between 1 and 65535.") + return settings.save_client_id(new_id) try: self.connector.reload_credentials()