Remote/headless OAuth authentication (#194)#198
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 05f63a45ff
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Pull request overview
This PR implements remote/headless OAuth authentication for basecamp auth login, solving the long-standing issue (#194) where the CLI hangs indefinitely when used over SSH because the loopback callback listener is unreachable from the remote browser.
Changes:
- SSH auto-detection + override flags:
IsRemoteSession()checks SSH env vars;--remote/--localflags allow explicit control.--remote/--localare mutually exclusive, enforced both via Cobra and in theLogin()function itself. - Paste-mode flow: In remote mode, the CLI prompts the user to open the auth URL in any browser, then paste the full callback URL back.
parseCallbackURLhandles whitespace/quote stripping, state validation, and OAuth error responses. - Context-aware input:
readCallbackInputuses a goroutine + buffered channel to support a 5-minute timeout over anyio.Reader.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
internal/hostutil/hostutil.go |
Adds IsRemoteSession() SSH detection via env vars |
internal/hostutil/hostutil_test.go |
Unit tests for IsRemoteSession() |
internal/auth/auth.go |
Core remote flow: new LoginOptions fields, defaults() auto-detection, Login() branching, parseCallbackURL, readCallbackInput |
internal/auth/auth_test.go |
Comprehensive tests: parseCallbackURL (9 cases), readCallbackInput (timeout/cancel), remote login integration, SSH auto-detect/override |
internal/commands/auth.go |
Adds --remote/--local flags to auth login and login commands |
internal/commands/profile.go |
Adds --remote/--local flags to profile create command |
internal/commands/profile_test.go |
Adds "remote", "local" to expected flags list |
e2e/auth.bats |
E2E tests verifying --remote/--local appear in help output |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
05f63a4 to
e82d65a
Compare
On SSH sessions the loopback OAuth callback can't reach the remote host's listener, so auth hangs forever. This adds a remote mode that prompts the user to paste the callback URL instead. - Add hostutil.IsRemoteSession() detecting SSH_CONNECTION/CLIENT/TTY - Add LoginOptions.Remote/Local/InputReader fields - Auto-detect SSH in defaults(); --local overrides detection - Add parseCallbackURL (strips quotes, validates state/error params) - Add readCallbackInput with context cancellation (distinct messages for timeout vs cancel) - Branch Login() into remote (paste prompt) vs local (listener) paths
Add --remote and --local flags to `basecamp login`, `basecamp auth login`, and `basecamp profile create`. Cobra enforces mutual exclusivity at the flag-parsing level.
e82d65a to
b5fbf98
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 9 changed files in this pull request and generated no new comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Summary
--remoteflag to force paste-mode,--localflag to override SSH auto-detectionDetails
basecamp auth loginstarts a loopback HTTP listener on127.0.0.1:8976. On remote/SSH sessions the browser runs on the user's local machine — the redirect hits local127.0.0.1, never reaches the remote's listener, and auth hangs forever.Remote mode flow:
SSH_CONNECTION/SSH_CLIENT/SSH_TTYenv vars (or--remoteflag)Changes:
internal/hostutil/hostutil.go—IsRemoteSession()SSH detectioninternal/auth/auth.go—LoginOptions.Remote/Local/InputReader,parseCallbackURL,readCallbackInput,Login()branchinginternal/commands/auth.go—--remote/--localflags onlogininternal/commands/profile.go—--remote/--localflags onprofile createTest plan
make checkpasses (fmt, vet, lint, test, e2e)go test -race ./internal/auth/passes — thread-safe log capture, deterministic channel-based synchronization (no sleeps)basecamp auth login --helpandbasecamp login --helpshow--remote/--localCloses #194