Login Flow Improvements and Fallback Handling · Entire
Review: PR #1366 — login: default to browser sign-in, add --device fallback
Verdict: Approve. Solid PR — clean structure, good test coverage at both unit and integration levels, careful comments, and the security fundamentals (PKCE S256, loopback-only listener, URL scheme validation, token validation before persist) are all in place. Unit tests pass locally. Two real-world UX gaps worth considering before or shortly after merge.
What it does
entire login now defaults to the RFC 8252 loopback authorization-code flow with PKCE (via auth-go v0.5.0's authcode package) when an interactive terminal is present, falling back to the device-code flow otherwise. --device forces the old flow. Both flows converge on a shared persistLogin tail (validate → keyring → contexts.json).
Strengths
- Good factoring. Extracting
persistLogininstead of duplicating the validate/save/record tail, and thebrowserAuthFlowinterface mirroring the existingdeviceAuthClientpattern, keepslogin.goconsistent with itself.shouldUseBrowserLoginas a pure function with a table test is the right shape. - Test design. The integration test's trick —
openBrowserreports failure under test so the fallback URL lands on stdout, then the test plays the browser against the loopback callback — exercises the real listener, real PKCE (code_verifierasserted at the token endpoint), and real state round-trip without spawning anything. Theinteractive.UnderTest()guards inwaitForEnterandopenBrowserfollow the documented project pattern. - Housekeeping done. The "before merge" item (re-pin from the auth-go branch pseudo-version to tagged v0.5.0) is already done in
go.mod/go.sum. Error wrapping preserves sentinels forerrors.Is(verified byTestRunBrowserLogin_WaitError), and the//nolint:wrapcheckcomments explain why.
Issues worth considering
SSH with a TTY gets a broken default (
cmd/entire/cli/login.go:79).CanPromptInteractively()is true overssh -t, so the browser flow is chosen — but the loopback listener binds 127.0.0.1 on the remote host, which the user's local browser can't reach. Even the printed fallback URL can't complete. The user's only recourse is Ctrl-C and--device.ghandgclouddetect this (e.g.SSH_CONNECTION/SSH_TTYenv vars) and route to the code flow. Suggest adding an SSH check toshouldUseBrowserLogin, or at minimum mentioning--devicein the "Waiting for sign-in..." context so a stuck user knows the escape hatch.Waithas no deadline (login.go:184). The device flow bounds waiting viaexpires_in(capped at 15 min); the browser flow waits on the command context, which has no timeout — if the user closes the browser tab without completing,entire loginhangs until Ctrl-C. Acontext.WithTimeoutaroundflow.Wait(5–15 min) would match the device flow's behavior and most peer CLIs.
Minor / non-blocking
- No fallback when the listener can't start (
login.go:80-83). IfStartBrowserAuthfails (port bind, firewall), the command errors out rather than falling back to the device flow. Falling back here would be friendlier and costs little. TestLogin_BrowserFlow_SavesTokendoesn't assert the saved token — it checks "Login complete." only. This matches the existingTestLogin_SavesTokenAfterApprovalconvention exactly, so it's fine as-is, but both names slightly overpromise; asserting theENTIRE_TEST_AUTH_STORE_FILEcontents would make them honest. Could be a follow-up touching both.waitForBrowserPrompt's 10s deadline only fires between reads (aReadStringblock ignores it) — but this mirrors the pre-existingwaitForLoginPrompt, and the pipe closes when the process exits, so it's not a new hazard.
Security notes
All good: PKCE with S256 (verified against prod discovery doc per the PR body), state round-tripped and validated by the authcode lib, openBrowser still rejects non-HTTP(S) URLs before the test guard, token iss/exp cross-checked before persisting, and the loopback redirect was verified registered server-side for any-port 127.0.0.1/callback. The full authorize URL (with PKCE challenge) deliberately isn't printed on the happy path — nice touch.