feat: browser login for Grok Build provider (#7013)#7735
Conversation
|
Warning You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again! |
|
Thanks for working on a smoother Grok Build login experience. I think the PKCE-specific work in this PR would be safer to consolidate into #7358 rather than merge the two PRs independently. #7358 already replaces the legacy Comparing the patches, five core files overlap:
More importantly, the implementations currently select mutually exclusive registry modes: #7358 uses At the same time, this PR is not a substitute for #7358's executor/model/discovery work, and #7358 does not contain this PR's PKCE UX. My suggestion is therefore to move the unique PKCE pieces from this PR into #7358 (or explicitly rebase this PR on #7358), then make one deliberate decision about auth ownership in that combined context. If both device-code and PKCE should remain available, the provider abstraction should first be extended to support multiple login flows instead of silently replacing the official device flow. That would preserve the broader |
Summary
Grok Build (
grok-cli) previously only supported a paste-token /auth.jsonimport flow (flowType: "import_token"). This adds a one-click browser PKCE login, reusing the sameauth.x.aiauthorize/token endpoints and publicgrok_idclient id already used by the siblingxai-oauthprovider (PR #7399), while keeping the existing paste-token path intact as a fallback for headless/remote installs.GROK_BUILD_OAUTH_CONFIGadded tosrc/lib/oauth/constants/oauth.ts, modeled onXAI_OAUTH_CONFIG— same auth.x.ai endpoints + public client id (via the existingresolvePublicCred("grok_id", ...), no new embedded credential — Hard Rule chore(ui): rebrand to OmniRoute #11), Grok-Build-scopedscope, and its own loopback port56122(distinct from xai-oauth's56121) so both flows can run concurrently.src/lib/oauth/providers/grok-cli-oauth.tsmodule:buildGrokBuildAuthUrl,exchangeGrokBuildToken, andmapGrokBuildBrowserTokens(mirrorsxai-oauth.ts, reuses itsdecodeXaiIdTokenIdentityhelper for id_token metadata).src/lib/oauth/providers/grok-cli.ts: the singlegrokCliregistry entry now carries both flows.flowTypeflips toauthorization_code_pkce(soGET authorize/exchange/poll-callbackdispatch correctly), andmapTokensauto-detects the input shape — a browser PKCE exchange response (access_token/refresh_token/...) vs. a pasted-token import ({ accessToken: <JWT or auth.json> }) — so theimport-tokenroute action (which callsmapTokensdirectly, bypassing the flow-type-aware helpers inproviders.ts) keeps working unmodified. I investigated whether the registry needed an action-keyed lookup (per the plan's open question) and found no existing precedent for that inproviders.ts/providers/index.ts— every entry is a flat single-flow object — so a unifiedmapTokensdispatch under one"grok-cli"key is the pattern-consistent choice.src/app/api/oauth/[provider]/[action]/route.ts: added"grok-cli"toPKCE_CALLBACK_PROVIDERS.IMPORT_TOKEN_PROVIDERS(paste-token) is untouched — both actions stay available.src/shared/components/OAuthModal.tsx: removed"grok-cli"fromIMPORT_TOKEN_ONLY_PROVIDERS(un-hides the Browser Login tab —windsurf/devin-cliuntouched, their Phase-2 fix is tracked separately), added it toPKCE_CALLBACK_SERVER_PROVIDERS, and added theredirectUribranch (http://127.0.0.1:56122/callback, merged into the existing xai-oauth branch to stay within the file-size ratchet).src/shared/constants/providers/oauth.ts: updatedgrok-cli'sauthHintcopy to mention both flows (no new i18n keys needed — same generic tab copy asxai-oauth/codex). Did not regeneratedocs/reference/PROVIDER_REFERENCE.md— running the generator pulled in a large amount of unrelated pre-existing provider-catalog drift (265→268 total, several renamed/added providers unrelated to this change); regenerating that file is out of scope here and left for a dedicated doc-sync pass.tests/unit/publicCreds.test.tshad no shape assertion for thegrok_idembedded default despite it being live in production (GROK_CLI_CONFIG/XAI_OAUTH_CONFIG/nowGROK_BUILD_OAUTH_CONFIG). Added one, following the existinggemini_id/windsurf_fbpattern (UUID-shape regex, no literal embedded).How validated
TDD — new/updated tests written before/alongside the implementation, all passing:
tests/unit/oauth-grok-cli-browser.test.ts(new, 12 tests):buildAuthUrlshape,exchangeTokenrequest/response handling + non-OK-response error (no stack leak), unifiedmapTokensdispatch (browser-shape vs paste-shape, plus the fix(grok-cli): parse expires_at from auth.json and exp from JWT to fix auto-refresh #5775 expiresIn clamp duplicated on the browser path), loopback-port collision guard (56122 vs 56121 vs codex's 1455), and route-level regression checks:GET /authorizereaches the PKCE branch (not the disabled-import_token branch),POST /exchangerequirescodeVerifier, a failed exchange returns a sanitized 500 (Hard Rule fix(ui): fix Select dropdown dark theme inconsistency #12 — asserted the upstream error body text does NOT leak into the response), andPOST /import-tokenstill works (no regression).tests/unit/oauth-modal-grok-cli-browser-login-7013.test.ts(new): source-level regression guard thatgrok-clileftIMPORT_TOKEN_ONLY_PROVIDERS,windsurf/devin-clistayed, andgrok-clijoinedPKCE_CALLBACK_SERVER_PROVIDERS.tests/unit/grok-cli-oauth.test.ts(updated, line-neutral for the one behavioral change): flipped theflowTypeassertion fromimport_tokentoauthorization_code_pkce; every paste-tokenmapTokenstest (JWT,auth.json, wrapped shapes, expiry clamps) still passes unmodified, proving the unified dispatch preserves the paste path.tests/unit/oauth-providers-config.test.ts(updated, 2 lines): the registry-conformance test'sEXPECTED_CONFIG_BY_PROVIDER["grok-cli"]now points atGROK_BUILD_OAUTH_CONFIG(this test assertsprovider.configidentity, which legitimately changes with this feature) + added aREQUIRED_FIELDS_BY_PROVIDER["grok-cli"]entry mirroringxai-oauth's.tests/unit/publicCreds.test.ts(gap-fix, +1 test):grok_idshape assertion.Red→green: ran every new/touched test file individually before and after each implementation step; all 119 tests across the touched OAuth suites pass (
oauth-grok-cli-browser,oauth-modal-grok-cli-browser-login-7013,grok-cli-oauth,grok-cli-oauth-test-supported-7610,oauth-providers-config,publicCreds,oauth-keychain-import-only-6041,windsurf-devin-executors,oauth-device-code-error-transparency,xai-oauth-provider). Fulltest:vitest:uisuite (927 tests, 173 files) also green.Gates run and green:
check-test-discovery.mjs,typecheck:core,typecheck:noimplicit:core(2 pre-existing unrelated errors incombo.ts/usageTracking.ts/cliRuntime.ts, none in touched files),eslint(touched files only),check:complexity-ratchets(both cyclomatic and cognitive within baseline),check:file-size.mjs(OAuthModal.tsx merged the xai-oauth/grok-cli redirect branches to stay within its frozen 998-line baseline instead of growing it),check:cycles,check:docs-sync,check:docs-counts(no regression — provider/OAuth counts unchanged by this PR).check-fabricated-docs.mjs --strictshows the pre-existing 3 unrelated stale claims (tracked in #7728), none of them mine.What was deferred
auth.x.aicannot be TDD'd — this is the untestable half per Hard Rule fix(ci): add environment for npm token access #18 item 2.docs/reference/PROVIDER_REFERENCE.mdregeneration — deferred to avoid pulling in unrelated pre-existing provider-catalog drift into this PR (see note above).live_check (required before merge — Hard Rule #18 item 2)
Deploy this branch to the VPS (
root@192.168.0.15), then:/deploy-vps-local(or the project's standard branch deploy) ontofeat/7013-grok-build-browser-login.grok-cli) connection card → "Sign in with browser" tab → complete theauth.x.ailogin.provider_connectionsrow is created withaccessToken/refreshToken/expiresAtpopulated (not the paste-token path).curl -s https://<vps-host>/v1/chat/completions -H "Authorization: Bearer <omniroute-api-key>" -H "Content-Type: application/json" -d '{"model":"grok-build","messages":[{"role":"user","content":"ping"}]}'— confirm a normal completion throughcli-chat-proxy.grok.com(proves the inference path is unaffected by the login-flow change).auth.jsonfor an existing or new connection.Refs #7013 (browser login shipped and unit-tested; issue stays open pending the VPS live_check above per Hard Rule #18 — closing here would be premature since the PKCE round-trip itself is not machine-verified in this PR).