← Back to PRs

#18737: fix(gemini-oauth): support cross-process PKCE flow in manual OAuth

by uzzero open 2026-02-17 01:06 View on GitHub →
extensions: google-gemini-cli-auth size: S
## 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