Files
automaker/apps/ui/src/components/views/settings-view/api-keys/hooks/use-api-key-management.ts
Test User c7ebdb1f80 feat: Implement API key authentication with rate limiting and secure comparison
- Added rate limiting to the authentication middleware to prevent brute-force attacks.
- Introduced a secure comparison function to mitigate timing attacks during API key validation.
- Created a new rate limiter class to track failed authentication attempts and block requests after exceeding the maximum allowed failures.
- Updated the authentication middleware to handle rate limiting and secure key comparison.
- Enhanced error handling for rate-limited requests, providing appropriate responses to clients.
2025-12-24 14:49:47 -05:00

174 lines
5.0 KiB
TypeScript

import { useState, useEffect } from 'react';
import { useAppStore } from '@/store/app-store';
import { getElectronAPI } from '@/lib/electron';
import type { ProviderConfigParams } from '@/config/api-providers';
interface TestResult {
success: boolean;
message: string;
}
interface ApiKeyStatus {
hasAnthropicKey: boolean;
hasGoogleKey: boolean;
}
/**
* Custom hook for managing API key state and operations
* Handles input values, visibility toggles, connection testing, and saving
*/
export function useApiKeyManagement() {
const { apiKeys, setApiKeys } = useAppStore();
// API key values
const [anthropicKey, setAnthropicKey] = useState(apiKeys.anthropic);
const [googleKey, setGoogleKey] = useState(apiKeys.google);
// Visibility toggles
const [showAnthropicKey, setShowAnthropicKey] = useState(false);
const [showGoogleKey, setShowGoogleKey] = useState(false);
// Test connection states
const [testingConnection, setTestingConnection] = useState(false);
const [testResult, setTestResult] = useState<TestResult | null>(null);
const [testingGeminiConnection, setTestingGeminiConnection] = useState(false);
const [geminiTestResult, setGeminiTestResult] = useState<TestResult | null>(null);
// API key status from environment
const [apiKeyStatus, setApiKeyStatus] = useState<ApiKeyStatus | null>(null);
// Save state
const [saved, setSaved] = useState(false);
// Sync local state with store
useEffect(() => {
setAnthropicKey(apiKeys.anthropic);
setGoogleKey(apiKeys.google);
}, [apiKeys]);
// Check API key status from environment on mount
useEffect(() => {
const checkApiKeyStatus = async () => {
const api = getElectronAPI();
if (api?.setup?.getApiKeys) {
try {
const status = await api.setup.getApiKeys();
if (status.success) {
setApiKeyStatus({
hasAnthropicKey: status.hasAnthropicKey,
hasGoogleKey: status.hasGoogleKey,
});
}
} catch {
// Silently handle API key status check failures to avoid exposing
// sensitive error details in the console
}
}
};
checkApiKeyStatus();
}, []);
// Test Anthropic/Claude connection
const handleTestAnthropicConnection = async () => {
setTestingConnection(true);
setTestResult(null);
try {
const api = getElectronAPI();
const data = await api.setup.verifyClaudeAuth('api_key');
if (data.success && data.authenticated) {
setTestResult({
success: true,
message: 'Connection successful! Claude responded.',
});
} else {
setTestResult({
success: false,
message: data.error || 'Failed to connect to Claude API.',
});
}
} catch {
setTestResult({
success: false,
message: 'Network error. Please check your connection.',
});
} finally {
setTestingConnection(false);
}
};
// Test Google/Gemini connection
// NOTE: Full API key validation requires a backend call to verify the key
// against Google's API. The current client-side validation only checks
// basic format requirements and cannot confirm the key is actually valid.
const handleTestGeminiConnection = async () => {
setTestingGeminiConnection(true);
setGeminiTestResult(null);
// Basic client-side format validation only
// This does NOT verify the key is valid with Google's API
if (!googleKey || googleKey.trim().length < 10) {
setGeminiTestResult({
success: false,
message: 'Please enter an API key with at least 10 characters.',
});
setTestingGeminiConnection(false);
return;
}
// Client-side validation cannot confirm key validity.
// The key will be verified when first used with the Gemini API.
setGeminiTestResult({
success: true,
message: 'API key format accepted. Key will be validated on first use with Gemini API.',
});
setTestingGeminiConnection(false);
};
// Save API keys
const handleSave = () => {
setApiKeys({
anthropic: anthropicKey,
google: googleKey,
});
setSaved(true);
setTimeout(() => setSaved(false), 2000);
};
// Build provider config params for buildProviderConfigs
const providerConfigParams: ProviderConfigParams = {
apiKeys,
anthropic: {
value: anthropicKey,
setValue: setAnthropicKey,
show: showAnthropicKey,
setShow: setShowAnthropicKey,
testing: testingConnection,
onTest: handleTestAnthropicConnection,
result: testResult,
},
google: {
value: googleKey,
setValue: setGoogleKey,
show: showGoogleKey,
setShow: setShowGoogleKey,
testing: testingGeminiConnection,
onTest: handleTestGeminiConnection,
result: geminiTestResult,
},
};
return {
// Provider config params for buildProviderConfigs
providerConfigParams,
// API key status from environment
apiKeyStatus,
// Save handler and state
handleSave,
saved,
};
}