Skip to content

Comments

Add Gemini Plan (OAuth) support#513

Merged
kevin-on merged 5 commits intomainfrom
feat/gemini-oauth
Jan 20, 2026
Merged

Add Gemini Plan (OAuth) support#513
kevin-on merged 5 commits intomainfrom
feat/gemini-oauth

Conversation

@kevin-on
Copy link
Collaborator

@kevin-on kevin-on commented Jan 20, 2026

Description

Summary

Adds support for connecting a Google AI subscription (Gemini Plan) to use Gemini Code Assist quota, similar to existing Anthropic and OpenAI plan connections.

Changes

New Provider Implementation

  • GeminiPlanProvider - Main provider that handles authentication and delegates to the adapter
  • GeminiCodeAssistAdapter - Translates requests to Gemini Code Assist API format (cloudcode-pa.googleapis.com)
  • geminiAuth.ts - OAuth flow with PKCE for Google authentication, including local callback server
  • geminiProject.ts - Manages project context discovery and onboarding for Code Assist

Type & Schema Updates

  • Added gemini-plan provider type to LLMProvider, ChatModel, and EmbeddingModel schemas
  • Extended thinking config support for gemini-plan models (same as regular Gemini)
  • Settings migration v15 → v16 to add the new provider and default models

UI

  • Added Gemini connection card in PlanConnectionsSection
  • ConnectGeminiPlanModal for OAuth login flow with automatic callback handling and manual URL fallback
  • Added warning banner about subscription OAuth risks (account restrictions have been reported for Anthropic)

Default Models

  • gemini-3-pro-preview (plan)
  • gemini-3-flash-preview (plan)

Technical Notes

  • Uses the same OAuth client credentials as Gemini CLI / opencode-gemini-auth
  • Adds thoughtSignature field to assistant tool calls to satisfy Code Assist API validation
  • Project context is cached per refresh token and invalidated on token refresh

Testing

  • Added unit tests for migration v15 → v16
  • Manual testing required for OAuth flow and API calls

Checklist before requesting a review

  • I have reviewed the guidelines for contributing to this repository.
  • I have performed a self-review of my code
  • I have performed a code linting check and type check (by running npm run lint:check and npm run type:check)
  • I have run the test suite (by running npm run test)
  • I have tested the functionality manually

Summary by CodeRabbit

  • New Features

    • Added Gemini Plan as a new LLM provider with OAuth-based connection and plan-aware models
    • Added Gemini 3 Pro and Gemini 3 Flash plan variants and UI to connect/disconnect the plan
  • Settings

    • Settings updated to surface Gemini Plan connections and model options; migration to include Gemini Plan defaults
  • Documentation

    • Added guidance on risks of connecting a Claude subscription via OAuth and usage cautions

✏️ Tip: You can customize this high-level summary in your review settings.

- Introduced Gemini plan as a new provider type with OAuth integration.
- Implemented authentication flow including token exchange and callback server.
- Updated settings schema to include Gemini provider and associated OAuth fields.
- Enhanced chat model settings to support Gemini-specific configurations.
- Added migration logic to transition existing settings to support the new provider.
- Comprehensive tests added for migration and Gemini functionality.
…Auth access for Claude subscriptions

- Added a warning in the README about the risks associated with connecting a Claude subscription, including potential account bans.
- Introduced a warning message in the PlanConnectionsSection to inform users about the restrictions on third-party OAuth access and advised caution in usage.
- Enhanced styles for warning messages to improve visibility and user awareness.
@coderabbitai
Copy link

coderabbitai bot commented Jan 20, 2026

📝 Walkthrough

Walkthrough

Adds Gemini Plan support: OAuth-based desktop PKCE flow, provider implementation, project onboarding/caching, code-assist adapter with streaming, UI to connect/disconnect Gemini Plan, schema updates, and a migration to include gemini-plan in defaults.

Changes

Cohort / File(s) Summary
Documentation & Styling
README.md, styles.css
Added a warning block about Claude OAuth restrictions and new CSS .smtcmp-settings-desc-warning.
OAuth & Auth Utilities
src/core/llm/geminiAuth.ts
New PKCE-based OAuth module: authorize URL builder, PKCE/state generation, token exchange, refresh, user email fetch, and a local callback server with lifecycle and timeout handling.
Provider Implementation
src/core/llm/geminiPlanProvider.ts, src/core/llm/geminiCodeAssistAdapter.ts, src/core/llm/gemini.ts
New GeminiPlanProvider integrating OAuth state, token refresh, project context, and adapter for Gemini Code Assist (streaming + non-streaming). Made select Gemini helpers public for adapter use.
Project Context & Onboarding
src/core/llm/geminiProject.ts
New project context resolver with caching, pending-request dedupe, load/onboard flows, onboarding retries, and exported helpers (invalidate/load/onboard/ensure).
UI: Settings & Modal
src/components/settings/modals/ConnectGeminiPlanModal.tsx, src/components/settings/sections/PlanConnectionsSection.tsx
New ConnectGeminiPlanModal supporting automatic (callback server) and manual redirect-URL flows; PlanConnectionsSection wired to show/connect/disconnect gemini-plan.
Constants & Defaults
src/constants.ts, src/core/llm/manager.ts
Added Gemini OAuth/constants, GEMINI plan provider entries in defaults, new provider type registration, and manager wiring to return GeminiPlanProvider.
Types & Schemas
src/types/provider.types.ts, src/types/chat-model.types.ts, src/types/embedding-model.types.ts, src/core/llm/gemini.types.ts
Added gemini-plan variants to provider/chat/embedding schemas and new code-assist request/response types.
Migrations & Tests
src/settings/schema/migrations/15_to_16.ts, src/settings/schema/migrations/15_to_16.test.ts, src/settings/schema/migrations/index.ts, src/settings/schema/migrations/14_to_15.ts
New migration 15→16 adding gemini-plan provider and chat models, test coverage, SETTINGS_SCHEMA_VERSION bump, and exported DEFAULT_CHAT_MODELS_V15.
Small Typing/UI Adjustments
src/components/settings/sections/models/ChatModelSettings.tsx, src/settings/schema/migrations/14_to_15.ts
Widened Gemini providerType checks to include gemini-plan; exported a migration constant.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Modal as ConnectGeminiPlanModal
    participant Server as Local Callback Server
    participant Google as Google OAuth Endpoint
    participant TokenExch as Token Exchange
    participant ProjAPI as Gemini Project API
    participant Adapter as GeminiCodeAssistAdapter

    rect rgba(100,150,255,0.5)
    User->>Modal: Click "Login to Google"
    Modal->>Modal: Generate PKCE & state
    Modal->>Google: Open authorize URL (code_challenge, scopes)
    Google-->>User: Consent screen
    User->>Google: Grant permission
    Google->>Server: Redirect with code & state
    Server->>Server: Validate state & respond UI
    Server-->>Modal: Resolve code
    end

    rect rgba(150,200,150,0.5)
    Modal->>TokenExch: Exchange code (pkce verifier)
    TokenExch-->>Modal: accessToken, refreshToken, expiresAt, email
    Modal->>ProjAPI: Load or onboard project (accessToken)
    ProjAPI-->>Modal: projectId / managedProjectId
    Modal->>Modal: Persist OAuth to provider settings
    end

    rect rgba(200,180,100,0.5)
    User->>Adapter: Request code-assist (headers with accessToken, projectId)
    alt access token expired
        Adapter->>TokenExch: Refresh token
        TokenExch-->>Adapter: New accessToken
    end
    Adapter-->>User: Stream or return generation
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Poem

🐰 I hopped in with keys and state,
PKCE charms to hesitate,
Tokens fetched and projects born,
Streams of code greet every morn,
A tiny rabbit cheers: connect, create!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.17% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title directly and accurately summarizes the main objective: adding Gemini Plan OAuth support as a new feature alongside existing plan integrations.
Description check ✅ Passed The pull request description comprehensively covers all required template sections including a clear summary, detailed changes, technical notes, and completed checklist items for testing and quality checks.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🤖 Fix all issues with AI agents
In `@src/components/settings/modals/ConnectGeminiPlanModal.tsx`:
- Around line 186-199: The current manual-connect flow fails because
ensureAuthContext may generate new PKCE/state that won't match a user-pasted
redirect URL (redirectState), causing the state check in the
ConnectGeminiPlanModal to always fail; update the logic in the block that calls
ensureAuthContext (using ensureAuthContext, effectivePkceVerifier,
effectiveState, redirectState, setManualError) so that when a redirectState is
present you parse/extract the original state from that redirect URL and use it
as the authoritative effectiveState (or skip calling ensureAuthContext when
manual paste is detected), ensure the pkceVerifier is obtained consistently for
that manual flow, and only run the mismatch error branch when neither a parsed
redirectState nor a valid ensured state exist.

In `@src/core/llm/geminiAuth.ts`:
- Around line 35-38: The startGeminiCallbackServer promise can hang if the
server is closed externally because its internal finalize callback is never
invoked; modify startGeminiCallbackServer to capture and store the finalize
resolver (e.g., a variable like currentGeminiFinalize) when creating the
auth-wait promise, and update stopGeminiCallbackServer to call that stored
finalize with an appropriate error when
isGeminiCallbackStopping/geminiCallbackServer is triggered so the awaiting
promise is rejected/cancelled immediately (also clear the stored finalize after
calling it and honor CALLBACK_TIMEOUT_MS as before).
- Around line 76-111: The code currently sends GEMINI_OAUTH_CLIENT_SECRET from
the client in exchangeGeminiCodeForTokens and refreshGeminiAccessToken; remove
the secret from these calls or move the token exchange to a secure backend:
either (A) verify that Google's Gemini/OAuth token endpoint accepts PKCE-only
flows and then delete GEMINI_OAUTH_CLIENT_SECRET usage from
exchangeGeminiCodeForTokens and refreshGeminiAccessToken (remove client_secret
parameter and ensure redirect_uri and code_verifier are retained), or (B)
implement a backend endpoint to perform the POST to
https://oauth2.googleapis.com/token (keeping GEMINI_OAUTH_CLIENT_SECRET
server-side) and change the client-side
exchangeGeminiCodeForTokens/refreshGeminiAccessToken to call that backend
instead; reference functions exchangeGeminiCodeForTokens,
refreshGeminiAccessToken and the constant GEMINI_OAUTH_CLIENT_SECRET when making
the change.

In `@src/core/llm/geminiPlanProvider.ts`:
- Around line 88-155: getAuthContext currently treats a missing expiresAt as
still valid and only invalidates the new refresh token after rotation; change
the expiry check in getAuthContext to trigger a refresh when expiresAt is
missing or expired (i.e. treat !this.provider.oauth.expiresAt the same as
expired) and when refreshing call invalidateProjectContextCache with the
previous refresh token before replacing provider.oauth (and also with the new
token if rotation occurred) so cache entries for the old token are cleared;
update the refresh flow around refreshGeminiAccessToken, updatedOauth
assignment, invalidateProjectContextCache and await
this.onProviderUpdate?.(this.provider.id, { oauth: updatedOauth }) accordingly
to use the old token for invalidation and handle token rotation.

In `@src/core/llm/geminiProject.ts`:
- Around line 116-147: The current loadManagedProject function swallows all
non-2xx responses and exceptions by returning null, which later causes
ProjectIdRequiredError to be thrown incorrectly; instead, on non-2xx responses
(in loadManagedProject) parse/collect the response status and body and throw a
distinct error (e.g., LoadProjectError) that includes status and diagnostic
info, and in the catch block rethrow or wrap the caught error in that same
LoadProjectError so callers can distinguish auth/network/load failures from a
genuine missing project id; update callers that expect null (where
ProjectIdRequiredError is raised) to treat this new error separately.
- Around line 149-203: The loop in onboardManagedProject always calls await
wait(delayMs) even after the final retry, causing an unnecessary delay; modify
the for loop so the wait is only invoked between attempts (e.g., call
wait(delayMs) only when attempt < attempts - 1) ensuring no delay after the last
iteration and preserving current retry/return logic.
🧹 Nitpick comments (5)
src/components/settings/sections/PlanConnectionsSection.tsx (1)

87-98: Consider expanding the warning scope to include Gemini.

The warning banner specifically mentions Anthropic OAuth restrictions, but since Gemini Plan also uses third-party OAuth (connecting to Google via a non-official client), similar risks may apply. Consider generalizing the warning or explicitly noting the risk status for Gemini.

src/core/llm/geminiCodeAssistAdapter.ts (2)

65-66: Prototype mutation for SDK compatibility is fragile but documented.

Using Object.setPrototypeOf to enable SDK helper methods (like response.text) on plain JSON is a clever workaround, but this pattern is fragile—if the SDK's GenerateContentResponse class changes its internal structure, this could silently break. Consider adding a comment noting this coupling.


140-141: Consider documenting the in-place mutation.

addThoughtSignatures mutates the contentEntries array's nested content.parts in place. While this works, adding a brief comment clarifying the mutation intent would improve readability.

src/components/settings/modals/ConnectGeminiPlanModal.tsx (2)

149-152: Consider logging errors for debugging.

Empty catch blocks with generic error messages make troubleshooting OAuth issues difficult. Consider logging the actual error (e.g., to console in development) while showing the user-friendly message.

💡 Suggested improvement
-    } catch {
+    } catch (err) {
+      console.error('Gemini OAuth auto-connect failed:', err)
       setAutoError(
         'Automatic connect failed. Paste the full redirect URL below and click "Connect with URL".',
       )

135-136: Popup blockers may prevent automatic OAuth flow.

window.open can be blocked by popup blockers, leaving the user waiting indefinitely. Consider detecting if the window opened successfully and guiding the user to allow popups if needed.

@kevin-on kevin-on merged commit d152d4d into main Jan 20, 2026
2 checks passed
@kevin-on kevin-on deleted the feat/gemini-oauth branch January 20, 2026 12:26
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