fix: Allow testing API keys without saving first

- Add optional apiKey parameter to verifyClaudeAuth endpoint
- Backend uses provided key when available, falls back to stored key
- Frontend passes current input value to test unsaved keys
- Add input validation before testing

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Kacper
2025-12-29 21:39:59 +01:00
parent 6c3d3aa111
commit fa23a7b8e2
4 changed files with 46 additions and 17 deletions

View File

@@ -71,10 +71,15 @@ function containsAuthError(text: string): boolean {
export function createVerifyClaudeAuthHandler() { export function createVerifyClaudeAuthHandler() {
return async (req: Request, res: Response): Promise<void> => { return async (req: Request, res: Response): Promise<void> => {
try { try {
// Get the auth method from the request body // Get the auth method and optional API key from the request body
const { authMethod } = req.body as { authMethod?: 'cli' | 'api_key' }; const { authMethod, apiKey } = req.body as {
authMethod?: 'cli' | 'api_key';
apiKey?: string;
};
logger.info(`[Setup] Verifying Claude authentication using method: ${authMethod || 'auto'}`); logger.info(
`[Setup] Verifying Claude authentication using method: ${authMethod || 'auto'}${apiKey ? ' (with provided key)' : ''}`
);
// Create an AbortController with a 30-second timeout // Create an AbortController with a 30-second timeout
const abortController = new AbortController(); const abortController = new AbortController();
@@ -94,14 +99,17 @@ export function createVerifyClaudeAuthHandler() {
delete process.env.ANTHROPIC_API_KEY; delete process.env.ANTHROPIC_API_KEY;
logger.info('[Setup] Cleared API key environment for CLI verification'); logger.info('[Setup] Cleared API key environment for CLI verification');
} else if (authMethod === 'api_key') { } else if (authMethod === 'api_key') {
// For API key verification, ensure we're using the stored API key // For API key verification, use provided key, stored key, or env var (in order of priority)
if (apiKey) {
// Use the provided API key (allows testing unsaved keys)
process.env.ANTHROPIC_API_KEY = apiKey;
logger.info('[Setup] Using provided API key for verification');
} else {
const storedApiKey = getApiKey('anthropic'); const storedApiKey = getApiKey('anthropic');
if (storedApiKey) { if (storedApiKey) {
process.env.ANTHROPIC_API_KEY = storedApiKey; process.env.ANTHROPIC_API_KEY = storedApiKey;
logger.info('[Setup] Using stored API key for verification'); logger.info('[Setup] Using stored API key for verification');
} else { } else if (!process.env.ANTHROPIC_API_KEY) {
// Check env var
if (!process.env.ANTHROPIC_API_KEY) {
res.json({ res.json({
success: true, success: true,
authenticated: false, authenticated: false,

View File

@@ -69,12 +69,22 @@ export function useApiKeyManagement() {
// Test Anthropic/Claude connection // Test Anthropic/Claude connection
const handleTestAnthropicConnection = async () => { const handleTestAnthropicConnection = async () => {
// Validate input first
if (!anthropicKey || anthropicKey.trim().length === 0) {
setTestResult({
success: false,
message: 'Please enter an API key to test.',
});
return;
}
setTestingConnection(true); setTestingConnection(true);
setTestResult(null); setTestResult(null);
try { try {
const api = getElectronAPI(); const api = getElectronAPI();
const data = await api.setup.verifyClaudeAuth('api_key'); // Pass the current input value to test unsaved keys
const data = await api.setup.verifyClaudeAuth('api_key', anthropicKey);
if (data.success && data.authenticated) { if (data.success && data.authenticated) {
setTestResult({ setTestResult({

View File

@@ -537,7 +537,10 @@ export interface ElectronAPI {
isMac: boolean; isMac: boolean;
isLinux: boolean; isLinux: boolean;
}>; }>;
verifyClaudeAuth: (authMethod?: 'cli' | 'api_key') => Promise<{ verifyClaudeAuth: (
authMethod?: 'cli' | 'api_key',
apiKey?: string
) => Promise<{
success: boolean; success: boolean;
authenticated: boolean; authenticated: boolean;
error?: string; error?: string;
@@ -1118,7 +1121,10 @@ interface SetupAPI {
isMac: boolean; isMac: boolean;
isLinux: boolean; isLinux: boolean;
}>; }>;
verifyClaudeAuth: (authMethod?: 'cli' | 'api_key') => Promise<{ verifyClaudeAuth: (
authMethod?: 'cli' | 'api_key',
apiKey?: string
) => Promise<{
success: boolean; success: boolean;
authenticated: boolean; authenticated: boolean;
error?: string; error?: string;
@@ -1208,8 +1214,12 @@ function createMockSetupAPI(): SetupAPI {
}; };
}, },
verifyClaudeAuth: async (authMethod?: 'cli' | 'api_key') => { verifyClaudeAuth: async (authMethod?: 'cli' | 'api_key', apiKey?: string) => {
console.log('[Mock] Verifying Claude auth with method:', authMethod); console.log(
'[Mock] Verifying Claude auth with method:',
authMethod,
apiKey ? '(with key)' : ''
);
// Mock always returns not authenticated // Mock always returns not authenticated
return { return {
success: true, success: true,

View File

@@ -491,12 +491,13 @@ export class HttpApiClient implements ElectronAPI {
}> => this.get('/api/setup/platform'), }> => this.get('/api/setup/platform'),
verifyClaudeAuth: ( verifyClaudeAuth: (
authMethod?: 'cli' | 'api_key' authMethod?: 'cli' | 'api_key',
apiKey?: string
): Promise<{ ): Promise<{
success: boolean; success: boolean;
authenticated: boolean; authenticated: boolean;
error?: string; error?: string;
}> => this.post('/api/setup/verify-claude-auth', { authMethod }), }> => this.post('/api/setup/verify-claude-auth', { authMethod, apiKey }),
getGhStatus: (): Promise<{ getGhStatus: (): Promise<{
success: boolean; success: boolean;