/** * Auth Section * Settings section for CLIProxy auth tokens (API key and management secret) */ import { useEffect, useState, useCallback } from 'react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Alert, AlertDescription } from '@/components/ui/alert'; import { ScrollArea } from '@/components/ui/scroll-area'; import { Switch } from '@/components/ui/switch'; import { RefreshCw, CheckCircle2, AlertCircle, Eye, EyeOff, RotateCcw, Sparkles, Copy, Check, KeyRound, ShieldCheck, ShieldAlert, Save, } from 'lucide-react'; import { useRawConfig } from '../hooks'; interface TokenInfo { value: string; isCustom: boolean; } interface AuthTokens { apiKey: TokenInfo; managementSecret: TokenInfo; } export default function AuthSection() { const { fetchRawConfig } = useRawConfig(); // State const [tokens, setTokens] = useState(null); const [loading, setLoading] = useState(true); const [saving, setSaving] = useState(false); const [error, setError] = useState(null); const [success, setSuccess] = useState(null); // Edit state const [showApiKey, setShowApiKey] = useState(false); const [showSecret, setShowSecret] = useState(false); const [editedApiKey, setEditedApiKey] = useState(null); const [editedSecret, setEditedSecret] = useState(null); const [copiedApiKey, setCopiedApiKey] = useState(false); const [copiedSecret, setCopiedSecret] = useState(false); const [agyAckBypass, setAgyAckBypass] = useState(false); const [agyAckBypassLoading, setAgyAckBypassLoading] = useState(true); const [agyAckBypassSaving, setAgyAckBypassSaving] = useState(false); // Fetch tokens const fetchTokens = useCallback(async () => { try { setLoading(true); setError(null); // Use /raw to get unmasked values for editing const response = await fetch('/api/settings/auth/tokens/raw'); if (!response.ok) { throw new Error('Failed to fetch auth tokens'); } const data = await response.json(); setTokens(data); } catch (err) { setError(err instanceof Error ? err.message : 'Unknown error'); } finally { setLoading(false); } }, []); const fetchAgyAckBypass = useCallback(async () => { try { setAgyAckBypassLoading(true); const response = await fetch('/api/settings/auth/antigravity-risk'); if (!response.ok) { throw new Error('Failed to load Antigravity power user settings'); } const data = (await response.json()) as { antigravityAckBypass?: boolean }; setAgyAckBypass(data.antigravityAckBypass === true); } catch (err) { setError(err instanceof Error ? err.message : 'Unknown error'); } finally { setAgyAckBypassLoading(false); } }, []); // Load on mount useEffect(() => { fetchTokens(); fetchAgyAckBypass(); fetchRawConfig(); }, [fetchTokens, fetchAgyAckBypass, fetchRawConfig]); // Clear success after timeout useEffect(() => { if (success) { const timer = setTimeout(() => setSuccess(null), 3000); return () => clearTimeout(timer); } }, [success]); // Clear error after timeout useEffect(() => { if (error) { const timer = setTimeout(() => setError(null), 5000); return () => clearTimeout(timer); } }, [error]); // Save all changes const saveChanges = async () => { const hasApiKeyChange = editedApiKey !== null && editedApiKey !== tokens?.apiKey.value; const hasSecretChange = editedSecret !== null && editedSecret !== tokens?.managementSecret.value; if (!hasApiKeyChange && !hasSecretChange) return; try { setSaving(true); setError(null); const payload: { apiKey?: string; managementSecret?: string } = {}; if (hasApiKeyChange) payload.apiKey = editedApiKey; if (hasSecretChange) payload.managementSecret = editedSecret; const response = await fetch('/api/settings/auth/tokens', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), }); if (!response.ok) { const data = await response.json(); throw new Error(data.error || 'Failed to save tokens'); } setSuccess('Tokens updated. Restart CLIProxy to apply.'); setEditedApiKey(null); setEditedSecret(null); await fetchTokens(); await fetchRawConfig(); } catch (err) { setError(err instanceof Error ? err.message : 'Unknown error'); } finally { setSaving(false); } }; // Regenerate management secret const regenerateSecret = async () => { try { setSaving(true); setError(null); const response = await fetch('/api/settings/auth/tokens/regenerate-secret', { method: 'POST', }); if (!response.ok) { const data = await response.json(); throw new Error(data.error || 'Failed to regenerate secret'); } setSuccess('New management secret generated. Restart CLIProxy to apply.'); await fetchTokens(); await fetchRawConfig(); } catch (err) { setError(err instanceof Error ? err.message : 'Unknown error'); } finally { setSaving(false); } }; // Reset to defaults const resetToDefaults = async () => { try { setSaving(true); setError(null); const response = await fetch('/api/settings/auth/tokens/reset', { method: 'POST', }); if (!response.ok) { const data = await response.json(); throw new Error(data.error || 'Failed to reset tokens'); } setSuccess('Tokens reset to defaults. Restart CLIProxy to apply.'); setEditedApiKey(null); setEditedSecret(null); await fetchTokens(); await fetchRawConfig(); } catch (err) { setError(err instanceof Error ? err.message : 'Unknown error'); } finally { setSaving(false); } }; // Copy to clipboard helpers const copyApiKey = async () => { if (!tokens) return; await navigator.clipboard.writeText(tokens.apiKey.value); setCopiedApiKey(true); setTimeout(() => setCopiedApiKey(false), 2000); }; const copySecret = async () => { if (!tokens) return; await navigator.clipboard.writeText(tokens.managementSecret.value); setCopiedSecret(true); setTimeout(() => setCopiedSecret(false), 2000); }; const saveAgyAckBypass = async (nextValue: boolean) => { if (nextValue) { const confirmed = window.confirm( 'Enable Antigravity power user mode?\n\nThis disables AGY responsibility checklist prompts in CLI and dashboard. You accept full responsibility for OAuth/account risk.' ); if (!confirmed) return; } try { setAgyAckBypassSaving(true); setError(null); const response = await fetch('/api/settings/auth/antigravity-risk', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ antigravityAckBypass: nextValue }), }); if (!response.ok) { const data = (await response.json()) as { error?: string }; throw new Error(data.error || 'Failed to update Antigravity power user mode'); } setAgyAckBypass(nextValue); setSuccess( nextValue ? 'Antigravity power user mode enabled.' : 'Antigravity power user mode disabled.' ); await fetchRawConfig(); } catch (err) { setError(err instanceof Error ? err.message : 'Unknown error'); } finally { setAgyAckBypassSaving(false); } }; if (loading || !tokens) { return (
Loading...
); } // Display values const displayApiKey = editedApiKey ?? tokens.apiKey.value; const displaySecret = editedSecret ?? tokens.managementSecret.value; // Check for unsaved changes const hasChanges = (editedApiKey !== null && editedApiKey !== tokens.apiKey.value) || (editedSecret !== null && editedSecret !== tokens.managementSecret.value); return ( <> {/* Toast-style alerts */}
{error && ( {error} )} {success && (
{success}
)}
{/* Scrollable Content */}

Configure CLIProxy authentication tokens. Changes require CLIProxy restart.

{/* API Key Section */}

API Key

{tokens.apiKey.isCustom && ( Custom )}

Used by Claude Code to authenticate with CLIProxy

setEditedApiKey(e.target.value)} placeholder="API key" disabled={saving} className="pr-20 font-mono text-sm" />
{/* Management Secret Section */}

Management Secret

{tokens.managementSecret.isCustom && ( Custom )}

Used by CCS dashboard to access CLIProxy management APIs

setEditedSecret(e.target.value)} placeholder="Management secret" disabled={saving} className="pr-20 font-mono text-sm" />
{/* Actions */}

Antigravity Power User Mode

Skip AGY responsibility checklists in Add Account and `ccs agy` flows.

Use only if you fully understand the OAuth suspension/ban risk pattern (#622). CCS cannot assume responsibility for account loss.

Resets both API key and management secret to their default values.

{/* Footer */}
); }