Skip to content

Bind the OAuth callback listener to an ephemeral loopback port (RFC 8252 §7.3) - #356

Merged
jeremy merged 1 commit into
mainfrom
security/oauth-loopback-ephemeral-port
Aug 28, 2026
Merged

Bind the OAuth callback listener to an ephemeral loopback port (RFC 8252 §7.3)#356
jeremy merged 1 commit into
mainfrom
security/oauth-loopback-ephemeral-port

Conversation

@jeremy

@jeremyjeremy commented Aug 28, 2026

Copy link
Copy Markdown
Member

What

hey auth login binds its OAuth callback listener to 127.0.0.1:0 instead of the fixed 127.0.0.1:8976, and the redirect_uri sent in both the authorization request and the token exchange carries whichever port the kernel handed out. The listener is bound before the authorization URL is built so the URL can name the real port; Login owns the listener and waitForCallback serves on it.

Why

RFC 8252 §7.3. A fixed port lets any local process squat 127.0.0.1:8976 and block the CLI from ever signing in. Hygiene / DoS-resistance only — a local process picks its own port either way, so this is not what stops loopback impersonation; PKCE is. Tracked on Security Hardening card 10248717570 (item 6 of the H1 #3945131 plan).

Depends on

basecamp/haystack#8704 — the server must accept any port for the CLI's loopback client. Deploy the server first; it stays compatible with the fixed port, so this can merge and ship in the next CLI release once that's live. A CLI built from this branch against a server without it gets redirect_uri mismatch.

Tests

  • TestLoginOAuthFlow: asserts Login listens on tcp127.0.0.1:0, that the listener is bound to a real 127.0.0.1 port, and that the same http://127.0.0.1:<port>/callback appears in the authorization URL and in the token exchange.
  • TestLoginListenFailure: a failed bind surfaces as failed to start callback server before any callback wait runs.
  • TestWaitForCallback* and the logger test now hand the listener in directly.
  • go test -race ./... green for internal/auth; golangci-lint clean. (internal/tui's open_remote_unix_test fails on this macOS sandbox on main too — unix socket path length — unrelated.)

AGENTS.md updated to stop naming the fixed port.


Summary by cubic

Binds the OAuth callback listener for hey auth login to an ephemeral loopback port (127.0.0.1:0) instead of the fixed 127.0.0.1:8976, so a local process can no longer squat the port and block sign-in. The redirect_uri now carries whichever port the kernel assigned, in both the authorization request and the token exchange.

Rollout

  • Requires the HEY server to accept any loopback port for the CLI's redirect_uri; deploy the server first — it stays compatible with the fixed port until this ships.
  • A CLI built from this branch against an older server fails with redirect_uri mismatch.
  • This is DoS-resistance only; it doesn't stop loopback impersonation, which PKCE already handles.

Written for commit f3deedc. Summary will update on new commits.

Review in cubic

…252 §7.3)
hey auth login listened on a fixed 127.0.0.1:8976, so any local process
holding that port could keep the CLI from ever signing in, and the
redirect_uri was a constant. Now the listener binds 127.0.0.1:0 before
the authorization URL is built and the redirect_uri carries whichever
port the kernel handed out, for both the authorization request and the
token exchange.
Hygiene only: a local process picks its own port either way, so this is
not what stops loopback impersonation; PKCE is. Requires the HEY server
to accept any port for the CLI's loopback client (haystack: OAuth loopback
redirect_uri matching), which deploys first and stays compatible with the
fixed port until this ships.
@jeremy
jeremy requested a review from a team as a code ownerAugust 28, 2026 10:13
CopilotAI balanced review requested due to automatic review settings August 28, 2026 10:13

CopilotAI 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

Binds OAuth callbacks to an ephemeral loopback port and consistently uses the assigned port throughout the PKCE flow.

Changes:

  • Moves listener creation into Login.
  • Passes the bound listener to callback handling.
  • Updates tests and authentication documentation.

Tip

If you aren't ready for review, convert to a draft PR.
Click "Convert to draft" or run gh pr ready --undo.
Click "Ready for review" or run gh pr ready to reengage.

Reviewed changes

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

FileDescription
internal/auth/auth.goUses an ephemeral callback port.
internal/auth/auth_test.goVerifies listener binding, redirect URI consistency, and failures.
AGENTS.mdDocuments ephemeral callback ports.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@jeremyjeremy mentioned this pull request Aug 28, 2026
@jeremy
jeremy merged commit dd96b39 into mainAug 28, 2026
24 checks passed
@jeremy
jeremy deleted the security/oauth-loopback-ephemeral-port branch August 28, 2026 16:37
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@jeremy