Cloud sign-in: Claude signs in on the approve, no code to paste
Merged
TE
2d ago+1100+1100+0123456789012345678901234567890123456789-97-97-01234567890123456789
Leo: "yeah that should be fine right? cuz man i dont wanna be liable for peoples' subscription tokns" β the Cloud sign-in should be one click plus the provider's approve page, with nothing else. #33014 made it one click, but Claude still needed a code pasted back. This removes the paste.
Spec: leo-workspace design/cloud.md (sign-in on the machine; we never hold a subscription token).
What changes
acpmux agent_login
claude auth loginruns withBROWSERset to a shim in the login's private 0700 folder. The shim writes the automatic sign-in URL Claude asks to open to a 0600 file and opens nothing.startanswers it asautoUrl+callbackPortonly when it is on an allowed sign-in host and its singleredirect_uriis exactlyhttp://localhost:<port>/callback. Otherwise the start is unchanged (manual URL + code).- A Claude
finishwith no code (only after an automatic start) waits forclaudeto exit on its own once its listener gets the approve. A pasted code still works as the fallback for the same login.
Host: a machine-bound sign-in tab
- New
browser.openIntargetcmux-chat-machine: on the person's click (or the start's one-shot sign-in credit), the host opens the automatic page in a Chromium tab whose localhost is bound to the chat's Cloud machine over loopback-forward-v1. - The tab uses the person's normal browser store, so their claude.com session is used. Its non-loopback pages keep the
.noLoopbackguard; the redirect tolocalhost:<port>/callbackreaches Claude's own listener on the machine. - cmux only carries the TCP bytes. It never reads or stores the code or the token. The binding is by page identity at adoption (it survives reroute and hibernation), capped at 64 entries.
- A bound tab never reaches this Mac's localhost: a bound tab whose machine is gone gets a blank store.
Card
- With
autoUrl, Sign in to Claude opens the page on the machine and shows "Approve on the sign-in page; it finishes on its own." There is no code field. - "Paste a code instead" is a quiet link. The card falls back to the code page by itself when the host can't open the bound tab (a WebKit-only host, or a machine without loopback-forward-v1) and on older apps.
Risk
- The OAuth code passes through a localhost URL in a browser tab, so browser history and tab records may hold it. It is single-use and PKCE-bound (useless without the verifier, which stays in
claudeon the machine). - Routing: only tabs adopted from this one open are bound. Other remote-tab routing is unchanged.
Tests
- acpmux unit tests: auto URL parsing, and the shim's file modes and symlink refusal.
- acpmux over-the-wire test: the fake claude calls
$BROWSER. It covers approve, paste while waiting, restart while waiting, and a run with no listener. - Webview:
cloud-signin-card.test.tsxcovers the approve flow, the fallbacks, paste instead, and a mismatched port. - Swift:
AgentPaneSignInPageTestscovers callback-port parsing, and that the machine open happens only with a host opener and the person's click.
Agent session: agent session
π€ Generated with Claude Code