Skip to content

Fix/desktop oauth connect opener#270

Merged
robdmac merged 13 commits into
mainfrom
fix/desktop-oauth-connect-opener
Jul 17, 2026
Merged

Fix/desktop oauth connect opener#270
robdmac merged 13 commits into
mainfrom
fix/desktop-oauth-connect-opener

Conversation

@robdmac

@robdmac robdmac commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Fixes the component sign in buttons on desktop

robdmac and others added 13 commits July 15, 2026 20:06
…ct buttons)

The integration connect buttons (Gmail/Calendar/GitHub/…) did nothing on
desktop. The frontend is a remote-origin webview (http://localhost:8788), and
Tauri v2's ACL rejects custom app commands from remote origins ("not allowed by
ACL" — same failure as the removed Switch-to-CLI button). So invoke("open_url")
threw, the error was swallowed in openExternalUrl, and the window.open fallback
is a no-op inside the webview → nothing happened.

Fix: use the official tauri-plugin-opener, whose opener:allow-open-url permission
is designed to work from remote webviews. Register the plugin, grant the
permission (scoped to http/https) in capabilities/default.json, and point
openExternalUrl at plugin:opener|open_url. This is the single chokepoint for all
connect buttons + cloud links, so it fixes every caller.

The custom open_url command is left in place (harmless) but is now unused.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014t8Ukp4NPWtqJ55X261wMZ
…n/workspace)

The opener plugin fixed URL-opening (integration connect buttons), but the cloud
sign-in + workspace + cloud-dashboard buttons invoke CUSTOM app commands
(sign_in_google_loopback, verify_orcabot_account, download_cloud_workspace, …),
which Tauri v2 also rejects from the remote-origin frontend ("not allowed by
ACL") — there was no app ACL manifest, so no permission existed to grant them.

Declare all 20 registered commands in build.rs (AppManifest::commands) to
generate their allow-<command> permissions, and grant every one in
capabilities/default.json. Because declaring a manifest makes ALL app commands
ACL-gated (including the local loading screen's get_ports/get_surface_token/…),
the full set is granted so both the local loading screen and the remote frontend
keep working. Keep build.rs's list in sync with generate_handler! in main.rs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014t8Ukp4NPWtqJ55X261wMZ
…ct handlers

All of WorkspaceBlock's connect buttons (Drive/GitHub/Box/OneDrive) and the file
picker were dead on desktop: they guarded on the reactive `user` from
`const { user } = useAuthStore()`, but WorkspaceBlock is a React Flow node that
can render before the desktop auth bootstrap sets the user and may not re-render
afterward — leaving `user` a stale null, so `if (!user) return` silently
dead-ended every handler. (TeamsBlock works because it guards on dashboardId and
gets identity from getAuthHeaders, which reads useAuthStore.getState() — always
current.)

Read the user via useAuthStore.getState() at click time in each handler, matching
how getAuthHeaders already works, so a stale render-time value can't block the
buttons.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014t8Ukp4NPWtqJ55X261wMZ
… is dead

Temporary diagnostic: the Workspace connect buttons fail silently on desktop even
after the getState fix, and the webview console isn't visible in release builds.
Toast the click + user/desktop/base state so the next test names the cause (no
click = onClick not firing / stale frontend; user=NULL = auth not resolved;
state-ok-but-no-browser = openExternalUrl). Revert once verified.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014t8Ukp4NPWtqJ55X261wMZ
…disabled

The real cause of the dead Workspace connect buttons: they're `disabled={!user}`
(8 of them) bound to the reactive `const { user } = useAuthStore()`. WorkspaceBlock
is a React Flow node that can render before desktop auth resolves and hold a stale
null, so every button stayed disabled — no click, no handler, no toast (which is
why instrumenting the handler showed nothing).

Read the user via a selector with a getState() fallback so `user` is always the
current store value. This un-disables the supportable connect buttons (Google
Drive/Gmail/Calendar via PKCE, GitHub device flow, Microsoft PKCE) at once.
(Box/Slack/Discord/X — confidential-secret providers desktop can't support — are
already hidden behind !DESKTOP_MODE from the #250 PKCE work.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014t8Ukp4NPWtqJ55X261wMZ
WKWebView keeps its HTTP cache across app launches, so a rebuilt/updated frontend
can serve stale JS chunks — which is why fixes that were verified in the staged
bundle never appeared in the running app (dead buttons, old UI, Box still shown).

On startup, before the loading screen redirects to the frontend, clear the
webview's browsing data when the frontend may have changed: on an app-version
change (a shipped frontend update) or ORCABOT_CLEAR_CACHE=1 (local iteration).
Gated via a version marker so it fires once per update, not every launch.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014t8Ukp4NPWtqJ55X261wMZ
…cache-clear

Root cause of the dead workspace buttons + visible Box + Calendar "access
revoked": DESKTOP_MODE was computed false in the packaged webview.
getDesktopMode() checked window.__TAURI__ (not reliably injected for a
remote-origin webview) and window.__TAURI_IPC__ (a Tauri v1 global, absent in v2),
so isTauri was false and DESKTOP_MODE fell back to a cookie/localStorage marker.
The startup log proves the webview works via __TAURI_INTERNALS__
(get_surface_token succeeded), so detect via that always-present v2 global.

With DESKTOP_MODE false, Box rendered (!DESKTOP_MODE), every connect handler took
the window.open web path (a no-op in the webview), and dev-auth degraded → the
Calendar revoked error. The earlier clear_all_browsing_data() cache-bust made it
worse by wiping the very cookie/localStorage DESKTOP_MODE relied on. Remove that
cache-clear entirely (the frontend loads fresh regardless — the log shows the
current chunks) and rely on robust Tauri detection instead.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014t8Ukp4NPWtqJ55X261wMZ
…indow.open

The dead workspace "Connect" buttons were in WorkspaceSidebar (the left WORKSPACE
panel's STORAGE INTEGRATIONS list) — a DIFFERENT component from WorkspaceBlock,
which is what I'd been fixing. Its handlers called openPopup → window.open
unconditionally, and window.open is a no-op inside the Tauri webview, so Drive/
GitHub/Box/OneDrive Connect did nothing on desktop (with a console "blocked" the
user never sees).

Branch openPopup on DESKTOP_MODE: on desktop, open the OS browser via
connectViaBrowser (opener plugin) and poll the provider status until connected
(each handler passes its checkConnected/onConnected); on web, keep the existing
popup + postMessage flow. Also drop the leftover diagnostic console.logs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014t8Ukp4NPWtqJ55X261wMZ
Strip the temporary "Drive connect clicked" toast (and its now-unused sonner
import) — it was a debugging aid and would pop for anyone using the canvas
Workspace block. The connect path itself is unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014t8Ukp4NPWtqJ55X261wMZ
Box OAuth needs a confidential client secret we can't ship safely in a desktop
binary (no PKCE/device public-client flow), so hide it on desktop — both the
icon-row button and the STORAGE INTEGRATIONS list entry are gated behind
!DESKTOP_MODE. Drive (Google PKCE), GitHub (device flow), and OneDrive (Microsoft
PKCE) remain — they're public-client and shippable. The Box folder-picker dialog
is unreachable on desktop once its openers are hidden, so it's left as-is.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014t8Ukp4NPWtqJ55X261wMZ
GitHub via the web-redirect connect (/integrations/github/connect) needs the
confidential GITHUB_CLIENT_SECRET, which we don't ship on desktop — so it errored
"GitHub OAuth is not configured." Desktop is a public client and must use the
device flow (like WorkspaceBlock): route handleGithubConnect to a GithubDeviceDialog
when DESKTOP_MODE, keep the redirect popup on web.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014t8Ukp4NPWtqJ55X261wMZ
Release payload = the desktop OAuth connect-button fixes on this branch:
DESKTOP_MODE detection via __TAURI_INTERNALS__, WorkspaceSidebar connect
buttons through the opener plugin (not window.open), GitHub device flow,
Box hidden. VM image unchanged (fetched on-demand via vm-image.json).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014t8Ukp4NPWtqJ55X261wMZ
- security: drop the custom `open_url` command grant from the remote-frontend
  capability. The frontend now opens URLs via the safer opener plugin; the custom
  command shells out (`cmd /C start` on Windows) on a merely prefix-validated URL,
  so granting it to the remote origin was an unnecessary injection vector. Matches
  the existing main.rs comment ("the custom open_url command which the ACL rejects
  from remote"). The command + its permission defs remain, just un-granted.
- ux: on desktop the connectViaBrowser onConnected callbacks now advance into the
  storage picker (Drive/OneDrive/GitHub), mirroring the web postMessage handlers,
  so users don't have to click the integration a second time to finish linking.
- leak: capture connectViaBrowser's cancel fn; cancel the prior in-flight poll on
  repeated clicks and on unmount, so the interval + focus listeners don't linger
  for the full 5-minute timeout.
- bump the mandated revision markers on both workspace components.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014t8Ukp4NPWtqJ55X261wMZ
@robdmac
robdmac merged commit 2c2a077 into main Jul 17, 2026
1 check passed
@robdmac
robdmac deleted the fix/desktop-oauth-connect-opener branch July 17, 2026 06:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant