Skip to content

Browser mode: login probe uses /backend-api/me (now Bearer-only) → false 'session not detected' for logged-in users #241

@hexsprite

Description

@hexsprite

Bug

Browser mode (--engine browser with --remote-chrome) fails the login check with ERROR: ChatGPT session not detected on every run, even when the connected Chrome profile is genuinely and fully signed into ChatGPT (chat history, custom GPTs, composer all render).

Verified on v0.13.0 (latest), connecting to a remote Chrome (--remote-chrome 127.0.0.1:<port>) whose profile is logged in.

Root cause

ensureLoggedIn (src/browser/actions/navigation.ts, buildLoginProbeExpression) probes login via:

const response = await fetch('/backend-api/me', { cache: 'no-store', credentials: 'include' });
status = response.status || 0;

ChatGPT migrated /backend-api/* to Bearer-token auth. A cookie-only fetch to /backend-api/me now returns 401 for every logged-in user (the SPA attaches an Authorization: Bearer <access_token> it fetches from /api/auth/session; a bare credentials: 'include' fetch has no bearer). It is no longer a cookie-authed endpoint.

The 0.13.0 DOM fallback (appAuthenticated) only rescues cfBlocked || status===429 || status===503 || status===0plain 401/403 are treated as authoritative "logged out" (comment: "Plain 401/403 remain authoritative because they can mean the ChatGPT session really expired."). That assumption is now wrong: a logged-in user gets 401 unconditionally.

Reproduction (run in the logged-in ChatGPT tab's console)

await fetch('/backend-api/me',      {credentials:'include',cache:'no-store'}).then(r=>r.status)   // => 401
await fetch('/api/auth/session',    {credentials:'include',cache:'no-store'}).then(r=>r.json()).then(j=>!!j.user)  // => true

/api/auth/session is still cookie-authed and returns { user, accessToken, ... } when signed in (and {} when not).

Suggested fix

Two viable options:

A. Accept 401/403 when the DOM proves an authenticated shell. appAuthenticated already requires a visible composer and (accounts-profile-button or a history-item), which a logged-out/guest session never has, so this can't false-positive:

const apiBlocked =
  cfBlocked || status === 429 || status === 503 || status === 0 ||
  status === 401 || status === 403;   // /backend-api/* now needs Bearer; cookie fetch 401s when logged in
const ok = !loginSignals && (status === 200 || (apiBlocked && appAuthenticated));

B. Probe the cookie-authed endpoint instead — fetch('/api/auth/session') and treat 200 + body.user as logged in.

Option A is the smaller diff and keeps the existing DOM safety gate.

Environment

Happy to send a PR for option A if useful.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1Urgent regression or broken agent/channel workflow affecting real users now.clawsweeper:fix-shape-clearClawSweeper found a clear likely implementation shape for this issue.clawsweeper:queueable-fixClawSweeper marked this issue as an existing queue_fix_pr work candidate.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.impact:auth-providerThis issue is about auth, provider routing, model choice, or SecretRef resolution.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions