#18737: fix(gemini-oauth): support cross-process PKCE flow in manual OAuth
extensions: google-gemini-cli-auth
size: S
Cluster:
OAuth and CLI Fixes
## Summary
- **Problem:** When using Gemini CLI OAuth in remote/VPS environments where localhost is unreachable, users must paste redirect URLs manually. If the URL comes from a different CLI run (e.g., the user opened the auth URL in another terminal session), the OAuth state check fails with "OAuth state mismatch - please try again", even though the auth code is perfectly valid.
- **Why it matters:** This is a real usability blocker for VPS/remote users who cannot bind localhost:8085. The flow is dead on arrival if the pasted URL doesn't originate from the exact same process.
- **What changed:** Instead of rejecting on state mismatch, the code now validates `parsed.state === verifier` first (same-process fast path). On mismatch, it falls back to using `parsed.state` as the `code_verifier`, since `buildAuthUrl` sets `state = verifier` by design. A log message informs the user that a cross-process flow was detected. Added tests for both same-process and cross-process manual OAuth flows.
- **What did NOT change (scope boundary):** The automatic localhost callback flow (`waitForLocalCallback`) is untouched. The PKCE challenge/verifier generation, `buildAuthUrl`, `parseCallbackInput`, and `exchangeCodeForTokens` signatures are unchanged. No new dependencies.
## Change Type (select all)
- [x] Bug fix
- [ ] Feature
- [ ] Refactor
- [ ] Docs
- [ ] Security hardening
- [ ] Chore/infra
## Scope (select all touched areas)
- [ ] Gateway / orchestration
- [ ] Skills / tool execution
- [x] Auth / tokens
- [ ] Memory / storage
- [x] Integrations
- [ ] API / contracts
- [ ] UI / DX
- [ ] CI/CD / infra
## Linked Issue/PR
- Related: N/A (discovered during VPS usage)
## User-visible / Behavior Changes
- Users in remote/VPS environments can now paste OAuth redirect URLs from a different CLI run and complete authentication successfully.
- A note is logged when a cross-process flow is detected: `"Note: using verifier from pasted URL (cross-process OAuth flow detected)."`
- The same-process flow behavior is unchanged.
## Security Impact (required)
- New permissions/capabilities? `No`
- Secrets/tokens handling changed? `No` — the PKCE verifier was already exposed via the `state` URL parameter by design (`buildAuthUrl` sets `state: verifier`). This change does not introduce new exposure.
- New/changed network calls? `No`
- Command/tool execution surface changed? `No`
- Data access scope changed? `No`
- **Analysis:** The state parameter check is removed as a hard gate but retained as a branch condition. In the cross-process path, `parsed.state` (from the URL) is used as `code_verifier`. Google's token endpoint still validates the `code_verifier` against the original PKCE `code_challenge` server-side, so a forged or incorrect state value simply fails the token exchange — there is no way to bypass PKCE validation. The CSRF protection role of `state` is not meaningful in a CLI context where the user manually pastes URLs.
## Repro + Verification
### Environment
- OS: macOS / Linux (VPS)
- Runtime/container: Node 22+
- Model/provider: N/A (OAuth flow, not model-dependent)
- Integration/channel: google-gemini-cli-auth extension
### Steps
1. Run `openclaw` on a VPS/remote environment (where localhost:8085 is not reachable from the browser)
2. Start Gemini CLI OAuth (`isRemote: true` triggers manual flow)
3. Open the auth URL in a local browser, complete sign-in
4. Copy the redirect URL from the browser
5. **Before:** paste it — get "OAuth state mismatch" if any delay caused the CLI to restart
6. **After:** paste it — authentication completes successfully with a log note
### Expected
- OAuth completes and returns valid credentials.
### Actual
- Before: "OAuth state mismatch - please try again" error.
- After: Credentials returned successfully.
## Evidence
- [x] Failing test/log before + passing after
- Added 2 new test cases: same-process flow (state matches) and cross-process flow (state mismatches, falls back to `parsed.state`)
## Human Verification (required)
- Verified scenarios: Both new tests pass; all 7 tests in `oauth.test.ts` pass. Verified the cross-process test correctly asserts `code_verifier` equals the foreign verifier (not the local one).
- Edge cases checked: Same-process flow remains identical (no behavioral change). Missing state parameter falls back via existing `parseCallbackInput` logic (`state ?? expectedState`).
- What I did **not** verify: Live end-to-end test against Google's token endpoint (would require real credentials).
## Compatibility / Migration
- Backward compatible? `Yes`
- Config/env changes? `No`
- Migration needed? `No`
## Failure Recovery (if this breaks)
- How to disable/revert this change quickly: Revert this single commit; the two code sites are self-contained.
- Files/config to restore: `extensions/google-gemini-cli-auth/oauth.ts`
- Known bad symptoms reviewers should watch for: If somehow a completely invalid `parsed.state` reaches `exchangeCodeForTokens`, Google will reject it with a token exchange error (not a silent failure).
## Risks and Mitigations
- Risk: A user could inadvertently paste an old/stale redirect URL with an expired auth code.
- Mitigation: Google's token endpoint rejects expired/used auth codes with a clear error, same as before.
---
*AI-assisted (Claude). Lightly tested via unit tests. The author understands the OAuth/PKCE flow and has verified the logic is correct.*
Made with [Cursor](https://cursor.com)
Most Similar PRs
#5027: fix(auth): use correct OAuth credentials for google-gemini-cli refresh
by shayan919293 · 2026-01-30
69.4%
#7781: fix: resolve Google Gemini CLI auth credential extraction #4585
by ManojPanda3 · 2026-02-03
68.8%
#16683: chore(onboarding): add explicit account-risk warning for Antigravit...
by vincentkoc · 2026-02-15
67.5%
#16786: fix: support google-antigravity OAuth for Gemini embeddings
by outsourc-e · 2026-02-15
67.4%
#4550: fix: sync google-gemini-cli-auth tokens from external CLI (#3803)
by SalimBinYousuf1 · 2026-01-30
66.6%
#8129: fix(auth): validate OAuth redirect URI to prevent open redirects
by yubrew · 2026-02-03
66.6%
#2657: fix: use TLS 1.2 for gemini-cli and google-antigravity OAuth reques...
by PrentissLiu · 2026-01-27
66.5%
#16684: fix:(antigravity): align Antigravity OAuth project discovery header...
by vincentkoc · 2026-02-15
66.4%
#3521: fix(gemini-auth): handle mise shims and nested node_modules paths
by sebslight · 2026-01-28
65.9%
#17392: Add testing infrastructure and expand gateway OAuth scopes
by jordanhubbard · 2026-02-15
65.0%