Skip to content

[workers-auth] Spawn cloudflared asynchronously so ctrl+c can interrupt Access authorization - #14838

Merged
edmundhung merged 4 commits into
cloudflare:mainfrom
TheSaiEaranti:fix-access-login-event-loop
Jul 29, 2026
Merged

[workers-auth] Spawn cloudflared asynchronously so ctrl+c can interrupt Access authorization#14838
edmundhung merged 4 commits into
cloudflare:mainfrom
TheSaiEaranti:fix-access-login-event-loop

Conversation

@TheSaiEaranti

@TheSaiEaranti TheSaiEaranti commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Fixes #12900.

When a domain is behind Cloudflare Access (e.g. during remote bindings startup), wrangler invokes cloudflared access login <domain>, which only exits once the user completes the authorization flow in the browser. getAccessHeaders() ran this via spawnSync, blocking Node's event loop for the entire wait. While blocked, no SIGINT listener and no raw-mode keypress handler can run, so ctrl+c does nothing — abandoning or accidentally closing the browser tab leaves a hung wrangler that must be killed from another terminal.

This PR spawns cloudflared asynchronously (preserving the stdout token-parsing contract and the spawn-failure → "please install cloudflared" error), keeping wrangler interruptible during the wait. A process exit hook kills a still-pending cloudflared so it doesn't outlive wrangler.

Notes:

  • Concurrent getAccessHeaders() calls for the same domain could already race past the header cache and invoke cloudflared more than once (the awaited Access-detection fetch yields between the cache check and the spawn). This PR doesn't change that; in-flight promise dedup would be a natural follow-up if it bites in practice.
  • End-to-end verification of the interactive flow requires a domain behind Cloudflare Access plus cloudflared installed; the tests model cloudflared with a fake child process, including a regression test that fails if the event loop is ever blocked again mid-authorization (it asserts a timer can fire while the login is pending).

  • Tests
    • Tests included/updated
    • Automated tests not possible - manual testing has been completed as follows:
    • Additional testing not necessary because:
  • Public documentation
    • Cloudflare docs PR(s):
    • Documentation not necessary because: this is a bug fix restoring expected ctrl+c behavior; no user-facing API or documented workflow changes.

Open in Devin Review

@changeset-bot

changeset-bot Bot commented Jul 24, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 40df592

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 5 packages
Name Type
@cloudflare/workers-auth Patch
wrangler Patch
@cloudflare/remote-bindings Patch
@cloudflare/vite-plugin Patch
@cloudflare/vitest-pool-workers Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@workers-devprod
workers-devprod requested review from a team and edmundhung and removed request for a team July 24, 2026 20:22
@workers-devprod

workers-devprod commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Codeowners approval required for this PR:

  • ✅ @cloudflare/wrangler
Show detailed file reviewers

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Open in Devin Review

@edmundhung edmundhung left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the fix!

I’m concerned that relying on the exit hook may be too late, since the running cloudflared process could itself prevent Wrangler from exiting. Could we pass the abort signal from the runtime controller through getAccessHeaders() to spawn() instead?

Relying on the process exit hook alone can be too late, since a running
cloudflared child can itself keep wrangler from exiting. Thread an optional
AbortSignal from RemoteRuntimeController through getAccessHeaders() into
spawn(), so tearing down the remote session kills a still-pending
authorization immediately and surfaces an AbortError, which the controller
already ignores. Both the child's error and close events settle the promise
as aborted, since their order after an abort kill is not guaranteed. The
exit hook remains as last-resort cleanup for callers without a signal.
@TheSaiEaranti

Copy link
Copy Markdown
Contributor Author

Good point, done in 6e47e5a. getAccessHeaders() now takes an optional signal and RemoteRuntimeController passes its own abort signal through to spawn(), so tearing down the session kills a pending cloudflared immediately instead of waiting for process exit. The abort surfaces as an AbortError, which the controller's existing catch already ignores.

Two details from implementing it: the promise treats both the child's error and close events as abort-aware, because after a signal kill their order isn't guaranteed and the close path would otherwise resolve with empty stdout. And I kept the exit hook as a last resort for call paths that don't have a signal, like the direct preview flow. Happy to drop it if you'd rather rely on the signal alone.

Tests cover the abort (child killed, AbortError propagated) and the workers-auth suite plus wrangler typecheck are green.

devin-ai-integration[bot]

This comment was marked as resolved.

@edmundhung edmundhung left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the fix!

@workers-devprod workers-devprod left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codeowners reviews satisfied

@github-project-automation github-project-automation Bot moved this from Untriaged to Approved in workers-sdk Jul 29, 2026
@pkg-pr-new

pkg-pr-new Bot commented Jul 29, 2026

Copy link
Copy Markdown
@cloudflare/autoconfig

npm i https://pkg.pr.new/@cloudflare/autoconfig@14838

@cloudflare/config

npm i https://pkg.pr.new/@cloudflare/config@14838

create-cloudflare

npm i https://pkg.pr.new/create-cloudflare@14838

@cloudflare/deploy-helpers

npm i https://pkg.pr.new/@cloudflare/deploy-helpers@14838

@cloudflare/kv-asset-handler

npm i https://pkg.pr.new/@cloudflare/kv-asset-handler@14838

miniflare

npm i https://pkg.pr.new/miniflare@14838

@cloudflare/pages-shared

npm i https://pkg.pr.new/@cloudflare/pages-shared@14838

@cloudflare/unenv-preset

npm i https://pkg.pr.new/@cloudflare/unenv-preset@14838

@cloudflare/vite-plugin

npm i https://pkg.pr.new/@cloudflare/vite-plugin@14838

@cloudflare/vitest-pool-workers

npm i https://pkg.pr.new/@cloudflare/vitest-pool-workers@14838

@cloudflare/workers-auth

npm i https://pkg.pr.new/@cloudflare/workers-auth@14838

@cloudflare/workers-editor-shared

npm i https://pkg.pr.new/@cloudflare/workers-editor-shared@14838

@cloudflare/workers-utils

npm i https://pkg.pr.new/@cloudflare/workers-utils@14838

wrangler

npm i https://pkg.pr.new/wrangler@14838

commit: 40df592

@edmundhung
edmundhung merged commit 8049ca4 into cloudflare:main Jul 29, 2026
75 of 78 checks passed
@github-project-automation github-project-automation Bot moved this from Approved to Done in workers-sdk Jul 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Remote bindings authorization hangs, ctrl+c cannot kill it

3 participants