Skip to content

login: harden the browser sign-in fallbacks - #1403

Merged
toothbrush merged 4 commits into
login-loopback-browser-flowfrom
login-browser-flow-hardening
Jun 10, 2026
Merged

login: harden the browser sign-in fallbacks#1403
toothbrush merged 4 commits into
login-loopback-browser-flowfrom
login-browser-flow-hardening

Conversation

@Soph

@Soph Soph commented Jun 10, 2026

Copy link
Copy Markdown
Collaborator

https://entire.io/gh/entireio/cli/trails/550

What

Hardening follow-ups to #1366 (browser sign-in default), from its code review. Stacked on login-loopback-browser-flow — review/merge that first.

  • Bound the loopback wait at 5 minutes. The device flow is bounded by the AS's expires_in; the browser flow waited forever if the user closed the tab. The timeout error points at entire login --device as the escape hatch, and Ctrl-C still reports as cancellation, not a timeout.
  • SSH sessions fall back to the device flow. Over ssh -t the browser flow was chosen (a TTY is present), but the loopback listener binds 127.0.0.1 on the remote host where the user's browser can't reach it. Detect SSH via the SSH_CONNECTION/SSH_CLIENT/SSH_TTY vars sshd sets — same as gh/gcloud — and explain the fallback in one line on stderr.
  • Listener-bind failure falls back to the device flow. If StartBrowserAuth can't bind (sandboxing, firewall, exhausted ports), warn and continue with the device flow instead of erroring out.

How

The flow choice moved out of the cobra RunE into runLoginAuto, which takes a loginFlowFacts struct (useDevice/canPrompt/sshSession, detected once at entry) plus a startBrowser func, so the selection logic and all three fallback paths are unit-tested with fakes. startLoginProcess in the integration tests now blanks the inherited SSH_* vars centrally, so browser-flow subprocess tests stay on the browser path even when the developer runs the suite over SSH.

Testing

  • New unit tests: timeout (incl. Ctrl-C-is-not-a-timeout), SSH/headless/listener-failure fallbacks and their stderr messages, --device produces no commentary, isSSHSession.
  • Existing browser-flow integration test still passes; mise run check green.

🤖 Generated with Claude Code


Note

Medium Risk
Changes authentication flow selection and timeout behavior for entire login, which is security-sensitive but limited to CLI UX and fallbacks with broad unit test coverage.

Overview
Hardens entire login browser sign-in with timeouts, SSH-aware routing, and automatic fallbacks to the device-code flow.

Browser wait is capped at 5 minutes (browserLoginTimeout). Loopback redirect wait uses a context timeout starting after the Enter prompt; deadline exceeded yields an error that suggests entire login --device, while parent cancellation still surfaces as context.Canceled, not a timeout.

Flow selection moves into runLoginAuto with a loginFlowFacts struct (--device, interactive TTY, SSH session). Browser flow is skipped when SSH env vars indicate a remote session (one-line stderr explanation), when headless, or when --device is set. If StartBrowserAuth fails to bind loopback, the CLI warns and falls back to device flow instead of failing outright.

Integration startLoginProcess clears inherited SSH_* vars so browser-flow subprocess tests stay stable when the suite runs over SSH. Unit tests cover SSH/headless/listener-failure paths, timeout vs cancel, and --device silence on stderr commentary.

Reviewed by Cursor Bugbot for commit 334df98. Configure here.

Soph and others added 4 commits June 10, 2026 21:47
The device flow is bounded by the AS's expires_in (capped at 15m), but
the browser flow waited on the loopback redirect with no deadline — a
closed tab left `entire login` hanging until Ctrl-C. Bound the wait at
5 minutes, starting after the Enter prompt so reading time isn't
counted, and point the timeout error at `--device` as the escape hatch.
Parent-context cancellation (Ctrl-C) still surfaces as cancellation,
not as a timeout.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Entire-Checkpoint: 8979d0646ea0
Over SSH with a TTY, CanPromptInteractively() is true so the browser
flow was chosen — but the loopback listener binds 127.0.0.1 on the
remote host, where the user's local browser can't reach it; even the
printed fallback URL can't complete. Detect SSH sessions via the
SSH_CONNECTION/SSH_CLIENT/SSH_TTY vars sshd sets and route them to the
device-code flow with a one-line explanation, the same way gh and
gcloud do.

Extract the flow choice from the cobra RunE into runLoginAuto, taking
a startBrowser func and the environment facts as plain values, so the
selection and its stderr commentary are unit-testable with fakes. The
browser-flow integration test blanks the SSH_* vars it inherits from
os.Environ() so it keeps exercising the browser path when a developer
runs the suite over SSH.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Entire-Checkpoint: 4aa664e559dc
If StartBrowserAuth couldn't bind the 127.0.0.1 listener (sandboxing,
firewall policy, exhausted ports), `entire login` errored out even
though the device-code flow would have worked fine. Warn on stderr with
the bind error and continue with the device flow instead of stranding
the user.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Entire-Checkpoint: 247398f9ecc7
Cleanups from a reuse/simplification/efficiency/altitude review of the
browser-flow hardening commits; no behavior change:

- Collapse runLoginAuto's three trailing bools into a loginFlowFacts
  struct, so call sites are self-documenting instead of relying on
  /* useDevice */-style comments.
- Pass canPrompt into runLogin instead of re-probing /dev/tty via
  interactive.CanPromptInteractively() a second time on the device path.
- Slim the startBrowser adapter closure to a direct return — the
  typed-nil interface on error is harmless since callers check err
  first.
- Move the SSH_* env blanking from the one browser-flow integration
  test into startLoginProcess, so future login subprocess tests are
  SSH-safe by default (same centralization idea as GitIsolatedEnv).
- Share one noopOpenURL test helper instead of seven inline lambdas.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Entire-Checkpoint: 224fb4e17934
Copilot AI review requested due to automatic review settings June 10, 2026 13:34

Copilot AI 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.

Pull request overview

Hardens entire login’s default browser (loopback) sign-in behavior by adding bounded waiting and more robust, user-friendly fallbacks to the device-code flow across SSH/headless and listener-bind failure scenarios.

Changes:

  • Add a 5-minute timeout to the browser-flow loopback redirect wait, with a timeout error that points users to entire login --device.
  • Route SSH sessions and headless/non-interactive environments to the device-code flow, emitting a one-line stderr explanation (except when --device is explicitly set).
  • Fall back to the device-code flow (with warning) when the browser-flow listener fails to start; add unit + integration test hardening (clearing inherited SSH_* env vars for subprocess tests).

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
cmd/entire/cli/login.go Adds runLoginAuto flow selection with SSH/headless detection, browser wait timeout, and device-flow fallbacks on browser listener start failures.
cmd/entire/cli/login_test.go Expands unit tests to cover flow selection, SSH detection, browser-start failure fallback messaging, and timeout vs cancellation behavior.
cmd/entire/cli/integration_test/login_test.go Stabilizes browser-flow subprocess integration tests by blanking inherited SSH_* environment variables by default.

@toothbrush toothbrush 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.

Ah, yes, these are all very sensible improvements i think 👍

@Soph
Soph marked this pull request as ready for review June 10, 2026 14:38
@Soph
Soph requested a review from a team as a code owner June 10, 2026 14:38
@toothbrush
toothbrush merged commit e40146c into login-loopback-browser-flow Jun 10, 2026
11 checks passed
@toothbrush
toothbrush deleted the login-browser-flow-hardening branch June 10, 2026 14:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants