1package auth
2
3import (
4 "context"
5 "fmt"
6 "net/http"
7 "time"
8)
9
10// ProbeAPIKey verifies that key is valid for provider by making a
11// lightweight authenticated request. Returns nil on success.
12func ProbeAPIKey(ctx context.Context, provider, key string) error {
13 if key == "" {
14 return fmt.Errorf("empty key")
15 }
16 c := &http.Client{Timeout: 15 * time.Second}
17 var req *http.Request
18 var err error
19
20 switch provider {
21 case "anthropic":
22 req, err = http.NewRequestWithContext(ctx, "GET", "https://api.anthropic.com/v1/models", nil)
23 if err != nil {
24 return err
25 }
26 req.Header.Set("x-api-key", key)
27 req.Header.Set("anthropic-version", "2023-06-01")
28 case "openai":
29 req, err = http.NewRequestWithContext(ctx, "GET", "https://api.openai.com/v1/models", nil)
30 if err != nil {
31 return err
32 }
33 req.Header.Set("authorization", "Bearer "+key)
34 case "kimi":
35 req, err = http.NewRequestWithContext(ctx, "GET", "https://api.kimi.com/coding/v1/models", nil)
36 if err != nil {
37 return err
38 }
39 req.Header.Set("authorization", "Bearer "+key)
40 case "deepseek":
41 req, err = http.NewRequestWithContext(ctx, "GET", "https://api.deepseek.com/v1/models", nil)
42 if err != nil {
43 return err
44 }
45 req.Header.Set("authorization", "Bearer "+key)
46 case "google":
47 // Google Generative Language: list models with the API key.
48 // Accepts the key via x-goog-api-key header (preferred over
49 // the ?key= query param so it doesn't show up in proxy logs).
50 req, err = http.NewRequestWithContext(ctx, "GET", "https://generativelanguage.googleapis.com/v1beta/models", nil)
51 if err != nil {
52 return err
53 }
54 req.Header.Set("x-goog-api-key", key)
55 default:
56 return fmt.Errorf("unknown provider %q", provider)
57 }
58
59 resp, err := c.Do(req)
60 if err != nil {
61 return fmt.Errorf("probe %s: %w", provider, err)
62 }
63 defer resp.Body.Close()
64 if resp.StatusCode == http.StatusUnauthorized || resp.StatusCode == http.StatusForbidden {
65 return fmt.Errorf("%s rejected the key (http %d)", provider, resp.StatusCode)
66 }
67 if resp.StatusCode >= 400 {
68 return fmt.Errorf("%s http %d", provider, resp.StatusCode)
69 }
70 return nil
71}