Skip to content

feat(cli): void github link — connect GitHub from any login provider (RFC 0074) - #230

Merged
Brooooooklyn merged 5 commits into
mainfrom
feat/github-identity-link
Jun 23, 2026
Merged

Brooooooklyn merged 5 commits into
mainfrom
feat/github-identity-link

Conversation

@Brooooooklyn

@Brooooooklyn Brooooooklyn commented Jun 23, 2026 •

Copy link
Copy Markdown
Member

What

Adds void github link and makes void github install work for any Void login (Google, GitHub, SSO) — the CLI side of RFC 0074 (decouple GitHub-for-deploys from the login provider). Pairs with the platform PR (void-platform) that adds the link routes; this PR is the framework/CLI half.

Changes

  • void github link — links the current Void account to a GitHub identity via a localhost + PKCE handshake (mirrors void auth login). Drives the platform's 3-endpoint flow: GET /github/link-url (authed) → GET /github/link-callback (public, returns a PKCE-bound link_code to localhost) → POST /github/link/complete (authed, binds to the bearer-token holder).
  • void github install auto-links — on a github_identity_required 403 it runs the link flow then retries once; any other error surfaces normally. Removes the old "run void auth login and choose GitHub" message (which risked forking a second account).
  • awaitBrowserCallback — extracted the localhost OAuth callback server from runLogin into a shared, tested helper with a finalize hook (the token/identity exchange runs in the request path, so the browser only shows success after it actually succeeds). runLogin is behavior-preserving.
  • Client methods getGithubLinkUrl / completeGithubLink; link wired into args/help/CLI; docs/reference/cli.md updated.

Security

The link flow uses deferred bind: the public callback never binds an identity — it only mints a stateless, PKCE-bound link_code delivered to the initiator's own localhost; the bind happens in the authenticated /complete keyed on the token holder. This defeats the account-linking confused-deputy (an attacker can only ever link their own identity). Reviewed via codex adversarial-review; findings fixed and test-pinned.

Tests

Feature unit tests green (auth-callback, auth-login regression, github-link/install/args/client). The repo's browser-based integration tests require Playwright browsers (unrelated to this CLI-only change).

Note

Requires the paired platform change; deploy needs <API>/github/link-callback registered as a GitHub App callback URL.

🤖 Generated with Claude Code


Note

Medium Risk
Touches account-linking and localhost OAuth callback behavior; mistakes could affect login UX or identity binding, though behavior is heavily unit-tested and defers binding to authenticated /complete.

Overview
Adds void github link so users logged in with Google or other SSO can bind a GitHub identity to their Void account via localhost + PKCE (same pattern as void auth login), using new platform APIs getGithubLinkUrl / completeGithubLink. 409 conflicts get clear messages for “GitHub already on another Void account” vs “Void account already has a different GitHub.”

void github install no longer tells users to re-login with GitHub on github_identity_required. It runs the link flow inline and retries the install URL once; other errors after linking still propagate normally.

Refactors OAuth localhost handling into shared awaitBrowserCallback with a finalize hook so token/link exchange runs in the request path before the browser sees success; runLogin is rewired on top with preserved ?token= / missing-code behavior. Exports createCodeVerifier / createCodeChallenge for the link flow.

Wires link through args, help, and CLI docs; adds unit tests for the callback helper, login exchange failure, install auto-link, link command, and client methods.

Reviewed by Cursor Bugbot for commit 63bd41b. Bugbot is set up for automated code reviews on this repo. Configure here.

Brooooooklyn and others added 5 commits June 23, 2026 13:32
Adds `void github link`, which links the logged-in Void account to a GitHub
identity by driving the platform's 3-endpoint link flow with a localhost +
PKCE handshake (mirrors `void auth login`).

- Extract the inline localhost-callback server from `runLogin` into a shared,
  exported `awaitBrowserCallback` helper; refactor `runLogin` to use it
  (behavior-preserving). Export the PKCE helpers `createCodeVerifier` /
  `createCodeChallenge`.
- Add `PlatformClient.getGithubLinkUrl` and `completeGithubLink`.
- Add `runGithubLink(root, client)`; wire `link` into github args, dispatch,
  CLI hint, help, and docs.
- New tests: `auth-callback.test.ts` (real coverage of the previously-untested
  localhost flow) and `github-link-cmd.test.ts` (conflict/missing-code paths),
  plus client + args coverage. Regression gate `auth-login` stays green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
When `void github install` returns a 403 with the stable code
`github_identity_required`, run the GitHub link flow inline and retry
the install URL exactly once instead of telling the user to
`void auth login` (which risks forking into a second account).

The link step is injectable (`runInstall(root, client, linkIdentity)`)
so tests can supply a fake linker without a real localhost server or
browser. Only a `github_identity_required` 403 triggers the auto-link;
every other error is rethrown unchanged. If the post-link retry still
fails, exit(1) with actionable guidance.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ovider

void github install now auto-runs the link flow on github_identity_required
(RFC 0074), so the install/link cross-references were stale. Clarify that
Deploy-on-GitHub works from any login provider after a one-time identity link.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ty_conflict

The link-complete server now returns two distinct 409 codes (RFC 0074 F1):
github_identity_conflict (the GitHub account belongs to another Void account)
vs github_already_linked (your account already has a different GitHub identity).
runGithubLink mapped any 409 to the former's wording; key on err.code so each
case shows the right message.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…tall retry errors

F3: awaitBrowserCallback now runs a generic finalize() hook inside the request
handler before the success page. On finalize success it shows the success page
and resolves with the result; on finalize throw it shows the failure page and
rejects. This restores base runLogin behavior (exchange in the request path,
failure page on exchange failure) and makes `void github link` show success
only after completeGithubLink succeeds. runLogin and runGithubLink refactored
onto the hook.

F4: runInstall's post-link retry no longer swallows all errors — it only keeps
the actionable link/install guidance when the retry is still a 403
github_identity_required, and rethrows every other failure (401/426/500/other).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@Brooooooklyn
Brooooooklyn merged commit cad3c32 into main Jun 23, 2026
7 checks passed
@Brooooooklyn
Brooooooklyn deleted the feat/github-identity-link branch June 23, 2026 06:40
cpojer pushed a commit that referenced this pull request Sep 11, 2026
Route-aware ISR TTL resolution is fully landed and deployed: framework
manifest.pages/pathRevalidate producer (#272) + api ingest/prewarm and dispatch
route-aware resolution (#228). Deferred follow-ups tracked in #230.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant