()
| 23 | } |
| 24 | |
| 25 | export function useApiKeyVerification(): ApiKeyVerificationResult { |
| 26 | const [status, setStatus] = useState<VerificationStatus>(() => { |
| 27 | if (!isAnthropicAuthEnabled() || isClaudeAISubscriber() || isCodexSubscriber()) { |
| 28 | return 'valid' |
| 29 | } |
| 30 | // Use skipRetrievingKeyFromApiKeyHelper to avoid executing apiKeyHelper |
| 31 | // before trust dialog is shown (security: prevents RCE via settings.json) |
| 32 | const { key, source } = getAnthropicApiKeyWithSource({ |
| 33 | skipRetrievingKeyFromApiKeyHelper: true, |
| 34 | }) |
| 35 | // If apiKeyHelper is configured, we have a key source even though we |
| 36 | // haven't executed it yet - return 'loading' to indicate we'll verify later |
| 37 | if (key || source === 'apiKeyHelper') { |
| 38 | return 'loading' |
| 39 | } |
| 40 | return 'missing' |
| 41 | }) |
| 42 | const [error, setError] = useState<Error | null>(null) |
| 43 | |
| 44 | const verify = useCallback(async (): Promise<void> => { |
| 45 | if (!isAnthropicAuthEnabled() || isClaudeAISubscriber() || isCodexSubscriber()) { |
| 46 | setStatus('valid') |
| 47 | return |
| 48 | } |
| 49 | // Warm the apiKeyHelper cache (no-op if not configured), then read from |
| 50 | // all sources. getAnthropicApiKeyWithSource() reads the now-warm cache. |
| 51 | await getApiKeyFromApiKeyHelper(getIsNonInteractiveSession()) |
| 52 | const { key: apiKey, source } = getAnthropicApiKeyWithSource() |
| 53 | if (!apiKey) { |
| 54 | if (source === 'apiKeyHelper') { |
| 55 | setStatus('error') |
| 56 | setError(new Error('API key helper did not return a valid key')) |
| 57 | return |
| 58 | } |
| 59 | const newStatus = 'missing' |
| 60 | setStatus(newStatus) |
| 61 | return |
| 62 | } |
| 63 | |
| 64 | try { |
| 65 | const isValid = await verifyApiKey(apiKey, false) |
| 66 | const newStatus = isValid ? 'valid' : 'invalid' |
| 67 | setStatus(newStatus) |
| 68 | return |
| 69 | } catch (error) { |
| 70 | // This happens when there an error response from the API but it's not an invalid API key error |
| 71 | // In this case, we still mark the API key as invalid - but we also log the error so we can |
| 72 | // display it to the user to be more helpful |
| 73 | setError(error as Error) |
| 74 | const newStatus = 'error' |
| 75 | setStatus(newStatus) |
| 76 | return |
| 77 | } |
| 78 | }, []) |
| 79 | |
| 80 | return { |
| 81 | status, |
| 82 | reverify: verify, |
no test coverage detected