diff --git a/internal/auth/request.go b/internal/auth/request.go index 23acb40..c0cdd52 100644 --- a/internal/auth/request.go +++ b/internal/auth/request.go @@ -190,9 +190,16 @@ func extractCallerToken(req *http.Request) string { if key := strings.TrimSpace(req.Header.Get("x-api-key")); key != "" { return key } + // Gemini/Google clients commonly send API key via x-goog-api-key. + if key := strings.TrimSpace(req.Header.Get("x-goog-api-key")); key != "" { + return key + } // Gemini AI Studio compatibility: allow query key fallback only when no // header-based credential is present. - return strings.TrimSpace(req.URL.Query().Get("key")) + if key := strings.TrimSpace(req.URL.Query().Get("key")); key != "" { + return key + } + return strings.TrimSpace(req.URL.Query().Get("api_key")) } func callerTokenID(token string) string { diff --git a/internal/auth/request_test.go b/internal/auth/request_test.go index 2eca44b..2f70e3f 100644 --- a/internal/auth/request_test.go +++ b/internal/auth/request_test.go @@ -130,6 +130,39 @@ func TestDetermineWithQueryKeyUsesDirectToken(t *testing.T) { } } +func TestDetermineWithXGoogAPIKeyUsesDirectToken(t *testing.T) { + r := newTestResolver(t) + req, _ := http.NewRequest(http.MethodPost, "/v1beta/models/gemini-2.5-pro:streamGenerateContent?alt=sse", nil) + req.Header.Set("x-goog-api-key", "goog-header-key") + + a, err := r.Determine(req) + if err != nil { + t.Fatalf("determine failed: %v", err) + } + if a.UseConfigToken { + t.Fatalf("expected direct token mode") + } + if a.DeepSeekToken != "goog-header-key" { + t.Fatalf("unexpected token: %q", a.DeepSeekToken) + } +} + +func TestDetermineWithAPIKeyQueryParamUsesDirectToken(t *testing.T) { + r := newTestResolver(t) + req, _ := http.NewRequest(http.MethodPost, "/v1beta/models/gemini-2.5-pro:generateContent?api_key=direct-api-key", nil) + + a, err := r.Determine(req) + if err != nil { + t.Fatalf("determine failed: %v", err) + } + if a.UseConfigToken { + t.Fatalf("expected direct token mode") + } + if a.DeepSeekToken != "direct-api-key" { + t.Fatalf("unexpected token: %q", a.DeepSeekToken) + } +} + func TestDetermineHeaderTokenPrecedenceOverQueryKey(t *testing.T) { r := newTestResolver(t) req, _ := http.NewRequest(http.MethodPost, "/v1beta/models/gemini-2.5-pro:generateContent?key=query-key", nil)