Skip to content

feat(xai): route OAuth login through the device-code flow#97249

Merged
fuller-stack-dev merged 6 commits into
openclaw:mainfrom
Jaaneek:xai-device-code-default
Jun 27, 2026
Merged

feat(xai): route OAuth login through the device-code flow#97249
fuller-stack-dev merged 6 commits into
openclaw:mainfrom
Jaaneek:xai-device-code-default

Conversation

@Jaaneek

@Jaaneek Jaaneek commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

What Problem This Solves

We had a lot of users reporting problems with the xAI OAuth callback. Sign-in relied on a localhost callback (127.0.0.1:56121), which fails on VPS, SSH, Docker, and other remote or headless setups, and also when Cloudflare sits in front of the callback. Affected users could not complete xAI/Grok OAuth at all.

Why This Change Was Made

The single xAI OAuth method (xai-oauth, method oauth) now runs the device-code flow: OpenClaw prints a verification URL plus a short code, the user approves in any browser, and the CLI polls for the token, with no localhost callback. The legacy callback implementation and the separate xai-device-code choice/method are removed so there is one OAuth path, still named "oauth". Token refresh also migrates credentials that still point at the retired /oauth/token endpoint, retries transient 5xx/429 and Cloudflare HTML-challenge responses, and fails fast on transport errors so a rotated refresh token is never resent.

User Impact

xAI/Grok OAuth now works from remote hosts (SSH, Docker, VPS) without a localhost callback. The choice is still presented as "xAI OAuth", and existing --method oauth / --auth-choice xai-oauth flows are unchanged. Credentials still pointing at the retired /oauth/token endpoint self-heal on the next refresh.

Evidence

  • pnpm test extensions/xai/xai-oauth.test.ts: 15 tests pass, covering device-code login without a localhost callback, stale-endpoint rediscovery, discovery-failure handling (no stale reuse), Cloudflare retry-then-succeed and retry-exhaustion, terminal-error no-retry, and refresh not being retried on transport errors.
  • pnpm tsgo:extensions clean; oxfmt --check clean.
  • Rebased onto latest openclaw/main (single commit).
  • Live: a real device-code sign-in against auth.x.ai completes with no localhost callback, access and refresh tokens are issued, and the account identity resolves. Note: the interactive openclaw models auth login command requires a TTY by design, so it is exercised manually rather than in automation.

Jaaneek added 2 commits June 27, 2026 13:55
Always use the device-code flow for xAI OAuth (no localhost callback) and drop
the separate device-code auth method/choice: the single xai-oauth method now
runs device code. Add retry + Cloudflare-challenge handling to the token
exchange and refresh, migrate stale /oauth/token endpoints on refresh, and drop
the now-unused authorization endpoint from discovery.
xAI rotates refresh tokens, so retrying a refresh_token grant after a lost
response could resend a token xAI already consumed and burn it, forcing a
re-login. Transport errors now fail fast; Cloudflare/5xx/429 responses are
still retried on the response path, where the refresh token was provably not
consumed.
@openclaw-barnacle openclaw-barnacle Bot added docs Improvements or additions to documentation extensions: xai size: L labels Jun 27, 2026
@clawsweeper

clawsweeper Bot commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed June 27, 2026, 11:59 AM ET / 15:59 UTC.

Summary
This PR routes xAI oauth/xai-oauth login through device-code polling, preserves device-code/xai-device-code as manual aliases, updates refresh endpoint/retry behavior, and revises xAI docs/tests.

PR surface: Source -51, Tests +18, Docs -8. Total -41 across 6 files.

Reproducibility: yes. for the underlying remote-login mismatch from source: current main's xAI oauth path waits for a localhost callback, while the PR head uses xAI device-code polling. No for the PR's after-fix live claim because no redacted artifact is inspectable in the PR body or comments.

Review metrics: 1 noteworthy metric.

  • Provider auth surface: 1 method behavior changed, 1 runtime alias retained, 1 manual-only choice alias retained. The PR changes public xAI login/setup behavior while preserving shipped entry points, so maintainers should review upgrade behavior beyond green tests.

Merge readiness
Overall: 🧂 unranked krab
Proof: 🧂 unranked krab
Patch quality: 🐚 platinum hermit
Result: blocked until real behavior proof is added.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • [P1] Add inspectable redacted terminal output, logs, screenshot, recording, or a linked artifact showing xAI device-code login completing after the patch.
  • Update the PR body so it reflects the restored device-code and xai-device-code aliases and the final refresh retry behavior.

Proof guidance:

  • [P1] Needs real behavior proof before merge: The PR body claims a live xAI sign-in, but no inspectable redacted terminal output, logs, screenshot, recording, or linked artifact is available for reviewer verification. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.

Risk before merge

  • [P1] Missing inspectable live proof remains a merge blocker for this external PR; unit tests and a textual live claim do not prove the real xAI account flow completed after the patch.
  • [P1] The PR changes a shipped auth-provider/setup contract by making oauth and xai-oauth run device-code instead of the loopback callback, so maintainers should explicitly accept the compatibility behavior even with aliases preserved.
  • [P1] The PR body is stale after later commits: it still says the legacy device-code choice/method are removed and says 5xx/429 refresh responses are retried, while the latest code preserves aliases and only retries Cloudflare-style HTML challenges.

Maintainer options:

  1. Require proof and confirm the alias plan (recommended)
    Before merge, require inspectable redacted device-code login proof, keep the restored device-code and xai-device-code aliases, and update the PR body to describe the current behavior.
  2. Accept the auth-provider behavior change
    Maintainers may intentionally land oauth as the new device-code-backed xAI login path, but that should be an explicit compatibility decision rather than inferred from green tests.
  3. Pause until live proof exists
    If the contributor cannot provide redacted proof from a real xAI setup, keep the PR open but blocked because unit tests cannot prove the external account flow.

Next step before merge

  • [P1] The remaining action is contributor-provided live proof, PR-body correction, and maintainer acceptance of auth-provider compatibility risk, not a narrow mechanical repair.

Security
Cleared: The diff is auth-sensitive but adds no dependencies, workflows, package metadata, secret exposure, or untrusted endpoint expansion; endpoint use remains restricted to trusted x.ai discovery values.

Review details

Best possible solution:

Merge only after redacted real device-code login proof is available, the PR body matches the latest alias/retry behavior, and maintainers explicitly accept xAI OAuth moving from loopback callback to device-code by default.

Do we have a high-confidence way to reproduce the issue?

Yes for the underlying remote-login mismatch from source: current main's xAI oauth path waits for a localhost callback, while the PR head uses xAI device-code polling. No for the PR's after-fix live claim because no redacted artifact is inspectable in the PR body or comments.

Is this the best way to solve the issue?

Mostly yes on owner boundary: xAI OAuth behavior belongs in the bundled xAI plugin, and the latest head preserves the shipped aliases. It is not merge-ready until live proof is inspectable, the PR body is corrected, and maintainers accept the auth-provider compatibility change.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against f857e8d66eb3.

Label changes

Label justifications:

  • P2: The PR affects one bundled provider's auth flow and upgrade path with limited blast radius, but mistakes can break real xAI login and saved-credential recovery.
  • merge-risk: 🚨 compatibility: The PR changes shipped xAI OAuth/setup entry points while preserving legacy device-code entry points as compatibility aliases.
  • merge-risk: 🚨 auth-provider: The PR changes xAI OAuth method routing, discovered token endpoint handling, and refresh retry behavior for saved credentials.
  • rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🧂 unranked krab and patch quality is 🐚 platinum hermit.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: The PR body claims a live xAI sign-in, but no inspectable redacted terminal output, logs, screenshot, recording, or linked artifact is available for reviewer verification. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
Evidence reviewed

PR surface:

Source -51, Tests +18, Docs -8. Total -41 across 6 files.

View PR surface stats
Area Files Added Removed Net
Source 2 150 201 -51
Tests 2 184 166 +18
Docs 2 19 27 -8
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 6 353 394 -41

What I checked:

  • Current main still uses loopback OAuth for xAI oauth: Current main's loginXaiOAuth waits on 127.0.0.1:56121 via waitForLocalOAuthCallback, which is the remote/headless failure mode the PR changes. (extensions/xai/xai-oauth.ts:535, f857e8d66eb3)
  • PR head reroutes oauth to device-code login: At PR head, createXaiOAuthAuthMethod() keeps method id oauth and choice id xai-oauth, but its run handler calls loginXaiDeviceCode. (extensions/xai/xai-oauth.ts:667, fef3cb24afb0)
  • PR head preserves the shipped device-code alias: The manifest keeps xai-device-code as a device-code auth choice with assistantVisibility: manual-only, and tests assert the runtime method and manifest choice remain present. (extensions/xai/openclaw.plugin.json:108, fef3cb24afb0)
  • CLI compatibility still depends on device-code resolving: openclaw models auth login --provider xai --device-code maps to method id device-code, so preserving that method is required for shipped CLI compatibility. (src/cli/models-cli.ts:345, f857e8d66eb3)
  • Maintainer review blocker was addressed by later commits: A maintainer review on commit 1ade018... asked the PR to preserve the shipped device-code auth method; later commits 25650cf..., 2908d4e..., and fef3cb... restored runtime and manifest aliases. (extensions/xai/index.test.ts:86, fef3cb24afb0)
  • Live discovery supports the device-code endpoint shape: Credential-free xAI OIDC discovery advertises device_authorization_endpoint, token_endpoint, and the OAuth device-code grant, matching the PR's endpoint assumptions.

Likely related people:

  • steipete: GitHub path history shows this account introduced the xAI Grok OAuth implementation and surrounding xAI provider/docs surface. (role: feature introducer; confidence: high; commits: af2b31319422, 7206811b80c4, 8bc927b2947e; files: extensions/xai/xai-oauth.ts, extensions/xai/index.ts, docs/providers/xai.md)
  • fuller-stack-dev: GitHub path history shows merged xAI device-code OAuth work, and this account reviewed the earlier PR head and added later alias-preservation commits on this branch. (role: recent area contributor and reviewer; confidence: high; commits: 896fd13b1c61, 65471a2da629, fef3cb24afb0; files: extensions/xai/xai-oauth.ts, extensions/xai/index.ts, extensions/xai/openclaw.plugin.json)
  • Jaaneek: This account authored the current PR and prior merged xAI OAuth refresh/callback fixes, so their connection is broader than opening this branch. (role: recent adjacent contributor; confidence: high; commits: 5f1df99a9c23, 0347ae48ea78, 6761d254e70e; files: extensions/xai/xai-oauth.ts, extensions/xai/xai-oauth.test.ts)
  • davemorin: GitHub path history shows merged xAI OAuth setup docs that established user-facing --device-code and xai-device-code guidance this PR updates. (role: recent docs contributor; confidence: medium; commits: a00e7d3898cf; files: docs/providers/xai.md, docs/tools/code-execution.md)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 auth-provider 🚨 May break OAuth, tokens, provider routing, model choice, or credentials. labels Jun 27, 2026
@fuller-stack-dev fuller-stack-dev self-assigned this Jun 27, 2026

@fuller-stack-dev fuller-stack-dev left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maintainer review for 1ade01850f68c78ffa0a75689f794b4e25f62cc6.

Findings:

  • [P1] Preserve the shipped xAI device-code auth method id before merging. src/cli/models-cli.ts still maps openclaw models auth login --provider xai --device-code to method: "device-code", and src/commands/models/auth.ts resolves that explicit method through pickAuthMethod. This PR removes the xAI device-code auth method from extensions/xai/index.ts, leaving the provider with only api-key and oauth; a focused probe at this head returned {"methods":["api-key","oauth"],"deviceCodeMatch":null,"oauthMatch":"oauth"}. Since the --device-code CLI path and xai-device-code onboarding choice shipped in releases, this breaks existing scripts/docs copied by users. Keep the new single visible OAuth behavior if desired, but preserve a deprecated device-code method alias that runs the same device-code login and add deprecatedChoiceIds: ["xai-device-code"] to the xai-oauth manifest choice so non-interactive onboarding gets a guided replacement instead of an unresolvable choice.

Best-fix judgment: routing xAI OAuth through device code is the right ownership boundary for headless/remote login, but the current patch is not the best merge shape because it deletes a shipped auth contract instead of deprecating it. Please preserve the shipped aliases and add focused regression tests for both --device-code and --auth-choice xai-device-code.

Proof I ran while reviewing:

  • git diff --check upstream/main...HEAD
  • node scripts/run-vitest.mjs extensions/xai/index.test.ts extensions/xai/xai-oauth.test.ts
  • node scripts/run-vitest.mjs src/cli/models-cli.test.ts -t 'maps --device-code'
  • node scripts/run-vitest.mjs src/commands/auth-choice.test.ts -t 'removed provider auth choice'
  • Focused provider-auth probe confirming device-code no longer resolves at this PR head

@fuller-stack-dev

Copy link
Copy Markdown
Member

Maintainer takeover update for exact head fef3cb24afb01cd1f69cf04ef67ed11d71dfadb3.

Fixes applied on top of the contributor branch:

  • Restored the legacy xai-device-code provider auth choice/method as a manual-only compatibility alias that dispatches to the same device-code OAuth flow as xai-oauth.
  • Preserved the runtime wizard.choiceId = "xai-device-code" mapping for callers that resolve auth choices from plugin runtime metadata instead of the manifest.
  • Tightened xAI refresh-token retry behavior so structured OAuth/service errors fail fast; only HTML/Cloudflare challenge responses remain retryable.

Verification on this head:

  • ./node_modules/.bin/oxfmt --write --threads=1 extensions/xai/index.ts extensions/xai/xai-oauth.ts extensions/xai/openclaw.plugin.json extensions/xai/index.test.ts extensions/xai/xai-oauth.test.ts
  • git diff --check
  • node scripts/run-vitest.mjs extensions/xai/index.test.ts extensions/xai/xai-oauth.test.ts
  • node scripts/run-vitest.mjs src/cli/models-cli.test.ts -t 'maps --device-code'
  • node scripts/run-vitest.mjs src/commands/auth-choice.test.ts -t 'removed provider auth choice'
  • Crabbox live smoke, provider local-container, lease cbx_107d71febbe7, Node 24 container, exact synced head fef3cb24afb01cd1f69cf04ef67ed11d71dfadb3: verified the manifest/runtime compatibility invariants, fetched xAI OAuth discovery from auth.x.ai, and successfully requested a real device authorization (status=200, verification host accounts.x.ai, expires_in=1800, interval=5). The smoke intentionally did not print transient device/user codes.
  • $autoreview after the Crabbox live smoke: clean, no accepted/actionable findings.

Known proof boundary: the final automated live smoke stops after xAI issues the device code. It does not complete a human account approval/token exchange in this maintainer run.

@fuller-stack-dev
fuller-stack-dev merged commit 84bcd50 into openclaw:main Jun 27, 2026
86 checks passed
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jun 28, 2026
Route xAI OAuth through device-code sign-in so remote and headless hosts do not need a localhost callback. Preserve the legacy manual `xai-device-code` auth choice/method as a compatibility alias to the same device-code flow.

Also migrate stale xAI token endpoints on refresh and fail fast on structured refresh errors while keeping retries scoped to detected HTML/Cloudflare challenge responses.

Verification:
- `node scripts/run-vitest.mjs extensions/xai/index.test.ts extensions/xai/xai-oauth.test.ts`
- `node scripts/run-vitest.mjs src/cli/models-cli.test.ts -t 'maps --device-code'`
- `node scripts/run-vitest.mjs src/commands/auth-choice.test.ts -t 'removed provider auth choice'`
- Crabbox local-container live smoke on exact head `fef3cb24afb01cd1f69cf04ef67ed11d71dfadb3`: xAI discovery and device authorization returned 200.
- `$autoreview` after the live smoke: clean.

Co-authored-by: Jaaneek <[email protected]>
Co-authored-by: fuller-stack-dev <[email protected]>
QiuYuang pushed a commit to QiuYuang/openclaw that referenced this pull request Jul 1, 2026
Route xAI OAuth through device-code sign-in so remote and headless hosts do not need a localhost callback. Preserve the legacy manual `xai-device-code` auth choice/method as a compatibility alias to the same device-code flow.

Also migrate stale xAI token endpoints on refresh and fail fast on structured refresh errors while keeping retries scoped to detected HTML/Cloudflare challenge responses.

Verification:
- `node scripts/run-vitest.mjs extensions/xai/index.test.ts extensions/xai/xai-oauth.test.ts`
- `node scripts/run-vitest.mjs src/cli/models-cli.test.ts -t 'maps --device-code'`
- `node scripts/run-vitest.mjs src/commands/auth-choice.test.ts -t 'removed provider auth choice'`
- Crabbox local-container live smoke on exact head `fef3cb24afb01cd1f69cf04ef67ed11d71dfadb3`: xAI discovery and device authorization returned 200.
- `$autoreview` after the live smoke: clean.

Co-authored-by: Jaaneek <[email protected]>
Co-authored-by: fuller-stack-dev <[email protected]>
chenyangjun-xy pushed a commit to chenyangjun-xy/openclaw that referenced this pull request Jul 1, 2026
Route xAI OAuth through device-code sign-in so remote and headless hosts do not need a localhost callback. Preserve the legacy manual `xai-device-code` auth choice/method as a compatibility alias to the same device-code flow.

Also migrate stale xAI token endpoints on refresh and fail fast on structured refresh errors while keeping retries scoped to detected HTML/Cloudflare challenge responses.

Verification:
- `node scripts/run-vitest.mjs extensions/xai/index.test.ts extensions/xai/xai-oauth.test.ts`
- `node scripts/run-vitest.mjs src/cli/models-cli.test.ts -t 'maps --device-code'`
- `node scripts/run-vitest.mjs src/commands/auth-choice.test.ts -t 'removed provider auth choice'`
- Crabbox local-container live smoke on exact head `fef3cb24afb01cd1f69cf04ef67ed11d71dfadb3`: xAI discovery and device authorization returned 200.
- `$autoreview` after the live smoke: clean.

Co-authored-by: Jaaneek <[email protected]>
Co-authored-by: fuller-stack-dev <[email protected]>
chenyangjun-xy pushed a commit to chenyangjun-xy/openclaw that referenced this pull request Jul 3, 2026
Route xAI OAuth through device-code sign-in so remote and headless hosts do not need a localhost callback. Preserve the legacy manual `xai-device-code` auth choice/method as a compatibility alias to the same device-code flow.

Also migrate stale xAI token endpoints on refresh and fail fast on structured refresh errors while keeping retries scoped to detected HTML/Cloudflare challenge responses.

Verification:
- `node scripts/run-vitest.mjs extensions/xai/index.test.ts extensions/xai/xai-oauth.test.ts`
- `node scripts/run-vitest.mjs src/cli/models-cli.test.ts -t 'maps --device-code'`
- `node scripts/run-vitest.mjs src/commands/auth-choice.test.ts -t 'removed provider auth choice'`
- Crabbox local-container live smoke on exact head `fef3cb24afb01cd1f69cf04ef67ed11d71dfadb3`: xAI discovery and device authorization returned 200.
- `$autoreview` after the live smoke: clean.

Co-authored-by: Jaaneek <[email protected]>
Co-authored-by: fuller-stack-dev <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs Improvements or additions to documentation extensions: xai merge-risk: 🚨 auth-provider 🚨 May break OAuth, tokens, provider routing, model choice, or credentials. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. P2 Normal backlog priority with limited blast radius. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. size: L status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants