From d98a1fc2d040f84e0053f959694257e28513180b Mon Sep 17 00:00:00 2001 From: Gemini AI Date: Sat, 27 Dec 2025 04:14:51 +0400 Subject: [PATCH] fix: Replace broken web OAuth with manual token entry for Antigravity - Removed Google OAuth popup flow (invalid_client error) - Added manual access token input option - Added instructions to use SDK mode with antigravity plugin (recommended) - Added copy button for CLI command --- .../settings/AntigravitySettings.tsx | 265 +++++++++--------- 1 file changed, 130 insertions(+), 135 deletions(-) diff --git a/packages/ui/src/components/settings/AntigravitySettings.tsx b/packages/ui/src/components/settings/AntigravitySettings.tsx index 032888c..22c2af5 100644 --- a/packages/ui/src/components/settings/AntigravitySettings.tsx +++ b/packages/ui/src/components/settings/AntigravitySettings.tsx @@ -1,5 +1,5 @@ import { Component, createSignal, onMount, For, Show } from 'solid-js' -import { Rocket, CheckCircle, XCircle, Loader, Sparkles, LogIn, LogOut, Shield } from 'lucide-solid' +import { Rocket, CheckCircle, XCircle, Loader, Sparkles, Key, LogOut, Shield, Terminal, Copy, ExternalLink } from 'lucide-solid' import { getUserScopedKey } from '../../lib/user-storage' interface AntigravityModel { @@ -22,15 +22,16 @@ interface AntigravityToken { } const ANTIGRAVITY_TOKEN_KEY = "antigravity_oauth_token" -const GOOGLE_OAUTH_CLIENT_ID = "782068742485-pf45b4gldtk7q847g3v5ercqfl31nkud.apps.googleusercontent.com" // Antigravity/Gemini CLI client const AntigravitySettings: Component = () => { const [models, setModels] = createSignal([]) const [isLoading, setIsLoading] = createSignal(true) const [connectionStatus, setConnectionStatus] = createSignal<'idle' | 'testing' | 'connected' | 'failed'>('idle') const [authStatus, setAuthStatus] = createSignal<'unknown' | 'authenticated' | 'unauthenticated'>('unknown') - const [isAuthenticating, setIsAuthenticating] = createSignal(false) const [error, setError] = createSignal(null) + const [showTokenInput, setShowTokenInput] = createSignal(false) + const [tokenInput, setTokenInput] = createSignal('') + const [copied, setCopied] = createSignal(false) // Check stored token on mount onMount(async () => { @@ -100,104 +101,45 @@ const AntigravitySettings: Component = () => { } } - const startGoogleOAuth = async () => { - setIsAuthenticating(true) - setError(null) + const handleTokenSubmit = () => { + const token = tokenInput().trim() + if (!token) { + setError('Please enter a valid token') + return + } try { - // Open Google OAuth in a new window - const redirectUri = `${window.location.origin}/auth/antigravity/callback` - const scope = encodeURIComponent("openid email profile https://www.googleapis.com/auth/cloud-platform") - const state = crypto.randomUUID() - - // Store state for verification - window.localStorage.setItem('antigravity_oauth_state', state) - - const authUrl = `https://accounts.google.com/o/oauth2/v2/auth?` + - `client_id=${GOOGLE_OAUTH_CLIENT_ID}&` + - `redirect_uri=${encodeURIComponent(redirectUri)}&` + - `response_type=token&` + - `scope=${scope}&` + - `state=${state}&` + - `prompt=consent` - - // Open popup - const width = 500 - const height = 600 - const left = (window.screen.width - width) / 2 - const top = (window.screen.height - height) / 2 - - const popup = window.open( - authUrl, - 'antigravity-oauth', - `width=${width},height=${height},left=${left},top=${top}` - ) - - if (!popup) { - throw new Error('Failed to open authentication window. Please allow popups.') + const tokenData: AntigravityToken = { + access_token: token, + expires_in: 3600, // 1 hour default + created_at: Date.now() } - // Poll for token in URL hash - const checkClosed = setInterval(() => { - try { - if (popup.closed) { - clearInterval(checkClosed) - setIsAuthenticating(false) - checkAuthStatus() - loadModels() - } else { - // Check if we can access the popup location (same origin after redirect) - const hash = popup.location.hash - if (hash && hash.includes('access_token')) { - const params = new URLSearchParams(hash.substring(1)) - const accessToken = params.get('access_token') - const expiresIn = parseInt(params.get('expires_in') || '3600', 10) + window.localStorage.setItem( + getUserScopedKey(ANTIGRAVITY_TOKEN_KEY), + JSON.stringify(tokenData) + ) - if (accessToken) { - const token: AntigravityToken = { - access_token: accessToken, - expires_in: expiresIn, - created_at: Date.now() - } - - window.localStorage.setItem( - getUserScopedKey(ANTIGRAVITY_TOKEN_KEY), - JSON.stringify(token) - ) - - popup.close() - clearInterval(checkClosed) - setIsAuthenticating(false) - setAuthStatus('authenticated') - loadModels() - } - } - } - } catch (e) { - // Cross-origin error - popup is on Google's domain, still waiting - } - }, 500) - - // Cleanup after 5 minutes - setTimeout(() => { - clearInterval(checkClosed) - if (!popup.closed) { - popup.close() - } - setIsAuthenticating(false) - }, 300000) - - } catch (err: any) { - console.error('OAuth error:', err) - setError(err.message || 'Authentication failed') - setIsAuthenticating(false) + setAuthStatus('authenticated') + setShowTokenInput(false) + setTokenInput('') + setError(null) + loadModels() + } catch (err) { + setError('Failed to save token') } } const signOut = () => { window.localStorage.removeItem(getUserScopedKey(ANTIGRAVITY_TOKEN_KEY)) setAuthStatus('unauthenticated') - setModels([]) + } + + const copyCommand = async () => { + const command = 'npx opencode-antigravity-auth login' + await navigator.clipboard.writeText(command) + setCopied(true) + setTimeout(() => setCopied(false), 2000) } const formatNumber = (num: number): string => { @@ -223,7 +165,7 @@ const AntigravitySettings: Component = () => {

Antigravity

-

Premium models via Google OAuth

+

Premium models via Google authentication

@@ -254,66 +196,118 @@ const AntigravitySettings: Component = () => {
-

Premium AI Models via Google

+

Premium AI Models

Antigravity provides access to Gemini 3 Pro/Flash, Claude Sonnet 4.5, Claude Opus 4.5, - and GPT-OSS 120B through Google's rate limits. Sign in with your Google account to get started. + and GPT-OSS 120B through Google's infrastructure.

{/* Authentication Section */} -
+
-

Google OAuth Authentication

+

Authentication

{authStatus() === 'authenticated' - ? 'You are signed in and can use Antigravity models' - : 'Sign in with Google to access premium models'} + ? 'Token configured - you can use Antigravity models' + : 'Configure authentication to access premium models'}

- - - -
- Authenticated + Configured
+ + + {/* SDK Mode Instructions */} +
+
+ + Recommended: Use OpenCode SDK Mode +
+

+ For the best experience, switch to OpenCode SDK mode in General settings. + The Antigravity plugin handles authentication automatically via Google OAuth. +

+
+ + npx opencode-antigravity-auth login + + +
+
+ + {/* Manual Token Entry */} +
+ + + + + +
+ +
+ setTokenInput(e.currentTarget.value)} + placeholder="Paste your Google OAuth access token..." + class="flex-1 px-3 py-2 bg-zinc-900 border border-zinc-700 rounded-lg text-sm text-white placeholder-zinc-500 focus:outline-none focus:border-purple-500" + /> + + +
+

+ You can get an access token by running the Antigravity auth plugin in a terminal, + or by extracting it from an authenticated Google Cloud session. +

+
+
+
+
{/* Error Display */} @@ -391,21 +385,9 @@ const AntigravitySettings: Component = () => {
- +
-

No models available at this time.

- -
-
- - -
-

Sign in with Google to see available models.

+

Models will be available after configuration.

@@ -414,13 +396,26 @@ const AntigravitySettings: Component = () => {

How to Use

    -
  • • Sign in with your Google account above
  • +
  • SDK Mode (Recommended): Switch to OpenCode binary mode and install the antigravity auth plugin
  • +
  • Native Mode: Enter your access token manually above
  • • Select any Antigravity model from the model picker in chat
  • • Models include Gemini 3, Claude Sonnet/Opus 4.5, and GPT-OSS
  • • Thinking-enabled models show step-by-step reasoning
  • -
  • • Uses Google's rate limits for maximum throughput
+ + {/* Plugin Link */} + ) }