Skip to content

OAuth browser window never opens on Windows #414

Description

@tanguylegazon

Craft Agents Version

0.7.5

Operating System

Windows 11

OS Version

Windows 11 24H2 (Build 26100)

AI Provider

Anthropic API (direct)

Model

Claude Opus 4.6, Claude Sonnet 4.6

Description

The built-in OAuth flow (Google, and potentially other providers) is broken on Windows. When clicking the "Sign in with Google" button (either from the onboarding wizard or when authenticating a source), the UI briefly flashes the message "Complete authentication in your browser", then silently reverts to the previous state — no browser window ever opens.

No error message is shown to the user. The failure is completely silent from the UI perspective.

The issue is persistent across:

  • App reinstalls
  • Version downgrades and upgrades
  • Deletion of ~/.craft-agent/credentials.enc
  • Multiple app restarts

Important: Sources that were already authenticated in a previous working installation continue to function normally (their stored refresh tokens are valid). Only new OAuth flows are affected — no browser ever opens.

This previously worked on the same machine with the same browser and network configuration — no firewall rules, proxy settings, or default browser were changed between the working state and the broken state. This rules out any environment-level cause (browser, firewall, OS network policy).

Steps to Reproduce

  1. Install Craft Agents v0.7.5 on Windows 11
  2. Connect to Claude AI via API
  3. Ask Craft Agents to connect a new Google source (e.g., Google Drive, Google Docs, Google Sheets) via OAuth
  4. Follow the given steps to create a working OAuth Client on Google Cloud Console
  5. Give OAuth Client ID and Secret to Craft Agents
  6. Click the "Sign in with Google" button
  7. The message "Complete authentication in your browser" appears for ~0.5 seconds
  8. No browser window opens — the UI silently reverts to the unauthenticated state

Expected Behavior

A browser window should open pointing to the Google OAuth consent screen, allowing the user to complete authentication.

Actual Behavior

The "Complete authentication in your browser" message appears for approximately 0.5 seconds and then disappears. No browser window opens. No error is displayed to the user. The OAuth flow fails silently and the source remains unauthenticated.

Screenshots / Screen Recordings

20260313-1511-12.0412540.mp4

Debug Logs

Captured from `%APPDATA%\@craft-agent\electron\logs\main.log` while reproducing the bug (source: `google-drive`, auth type: `oauth-google`):


14:51:59.924 [pi] PreToolUse request: mcp__session__source_google_oauth_trigger
14:51:59.932 [session] Auth request for session 260313-mild-reef: oauth-google google-drive
14:51:59.933 [session] Force-aborting after auth request for session 260313-mild-reef
14:51:59.936 [session] tool_start: mcp__session__source_google_oauth_trigger
14:51:59.945 [session] Chat loop exited unexpectedly
14:51:59.949 [session] [pi] Session tool completed: source_google_oauth_trigger (isError=false)
--- [NO further OAuth or browser-related log entries in main.log] ---
14:52:00.453 [PersistenceQueue] Wrote session (last write before ~14s gap)


**Critical observation:** The main process correctly receives and dispatches the auth request. The session is force-aborted as expected. But there is **zero log activity** after that in `main.log` — no `oauth:start`, no `shell.openExternal`, no callback server creation, no error. The renderer/preload-side OAuth handling produces **no output in main.log at all**, making the failure completely invisible at the main process level.

Additional Context

(AI generated)

Technical Analysis

Debug log analysis confirms: The main process side of the OAuth flow works correctly end-to-end:

  1. source_google_oauth_trigger tool is called
  2. ✅ Auth request is dispatched to the renderer: Auth request for session ...: oauth-google google-drive
  3. ✅ Session is force-aborted (expected, to pause the agent while OAuth completes)
  4. ✅ Tool completes with isError=false

But after step 2, main.log is completely silent — no oauth:start, no shell.openExternal, no callback server creation, no error from the renderer.

Root cause hypothesis: The performOAuth function in bootstrap-preload.cjs (which runs in the Electron renderer/preload context) either:

  • Fails immediately (within ~500ms, matching the observed UI "blink") with an unlogged error
  • Never receives the auth request IPC event from the main process

The preload code responsible for opening the browser:

api.performOAuth = async (args) => {
  let callbackServer = null;
  try {
    callbackServer = await createCallbackServer({ appType: "electron" });  // HTTP server via Node.js
    const port = parseInt(new URL(callbackServer.url).port, 10);
    const startResult = await client.invoke("oauth:start", { sourceSlug, callbackPort: port, ... });
    await import_electron2.shell.openExternal(startResult.authUrl);  // ← likely fails here silently
    const callback = await callbackServer.promise;
    ...
  } catch (err) {
    // Error swallowed — user sees nothing
    return { success: false, error: err instanceof Error ? err.message : "OAuth flow failed" };
  } finally {
    callbackServer?.close();
  }
};

Key asymmetry between preload and main process:

The main.cjs has a robust openUrl() with a fallback for when shell.openExternal fails:

async function openUrl(url) {
  try {
    const { shell } = await import("electron");
    await shell.openExternal(url);
    return;
  } catch {} // silently swallowed, then falls back to 'open' npm package
  await openFn(url); // platform-specific browser launch via npm 'open'
}

But bootstrap-preload.cjs calls shell.openExternal directly with no fallback — on Windows, if this call fails, the error is silently caught and the user sees only the ~500ms flash.

Logging gap: The preload/renderer process does not write to main.log. Any errors in performOAuth are invisible in the standard debug log file. The renderer DevTools console would likely show the actual error.

Why it worked in a previous installation:
Previously authenticated sources use stored refresh tokens (auto-refreshed on session start — confirmed in logs: Refreshed Google token for google-docs). They never re-trigger performOAuth. Only new OAuth flows fail.

Possible root causes (for investigation):

  • shell.openExternal failing silently in the preload context on Windows (sandbox restrictions, no registered https:// handler, Windows 11 Smart App Control)
  • createCallbackServer() failing immediately (Node.js http module unavailable in sandboxed preload, or port conflict)
  • A regression introduced when the OAuth callback handling was moved from the main process to the preload, losing the openUrl() fallback
  • The auth request IPC event not being properly received by the renderer window

Workaround

None found. As a workaround, sources that were authenticated in a previous installation can continue to be used until their tokens expire.

Workspace Config

  • Using local workspace (~/.craft-agent/workspaces/my-workspace)
  • Active sources: google-docs, google-sheets (authenticated in previous installation)
  • Sources needing re-auth: google-drive, google-slides, gmail (all fail)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingv0.7.7Fixed in v0.7.7

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions