From 9985c4a344a64b707ae8c18833790926ac1ace03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9F=B4=E5=8F=81?= Date: Wed, 4 Feb 2026 16:26:36 +0800 Subject: [PATCH] =?UTF-8?q?fix(gemini):=20=E4=BC=98=E5=8C=96=20Gemini=20?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E8=AE=A4=E8=AF=81=E5=85=BC=E5=AE=B9=E6=80=A7?= =?UTF-8?q?=EF=BC=8C=E6=94=AF=E6=8C=81=20Authorization:=20Bearer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 调整 API key 提取优先级,让 /v1beta 接口同时支持 x-goog-api-key 和 Authorization: Bearer 两种认证方式,解决 OpenClaw 等使用 Bearer 认证 的客户端无法直接访问 Gemini 接口的问题。 --- .../server/middleware/api_key_auth_google.go | 37 +++++++++++++------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/backend/internal/server/middleware/api_key_auth_google.go b/backend/internal/server/middleware/api_key_auth_google.go index 1a0b0dd5..38fbe38b 100644 --- a/backend/internal/server/middleware/api_key_auth_google.go +++ b/backend/internal/server/middleware/api_key_auth_google.go @@ -26,7 +26,7 @@ func APIKeyAuthWithSubscriptionGoogle(apiKeyService *service.APIKeyService, subs abortWithGoogleError(c, 400, "Query parameter api_key is deprecated. Use Authorization header or key instead.") return } - apiKeyString := extractAPIKeyFromRequest(c) + apiKeyString := extractAPIKeyForGoogle(c) if apiKeyString == "" { abortWithGoogleError(c, 401, "API key is required") return @@ -108,25 +108,38 @@ func APIKeyAuthWithSubscriptionGoogle(apiKeyService *service.APIKeyService, subs } } -func extractAPIKeyFromRequest(c *gin.Context) string { - authHeader := c.GetHeader("Authorization") - if authHeader != "" { - parts := strings.SplitN(authHeader, " ", 2) - if len(parts) == 2 && parts[0] == "Bearer" && strings.TrimSpace(parts[1]) != "" { - return strings.TrimSpace(parts[1]) +// extractAPIKeyForGoogle extracts API key for Google/Gemini endpoints. +// Priority: x-goog-api-key > Authorization: Bearer > x-api-key > query key +// This allows OpenClaw and other clients using Bearer auth to work with Gemini endpoints. +func extractAPIKeyForGoogle(c *gin.Context) string { + // 1) preferred: Gemini native header + if k := strings.TrimSpace(c.GetHeader("x-goog-api-key")); k != "" { + return k + } + + // 2) fallback: Authorization: Bearer + auth := strings.TrimSpace(c.GetHeader("Authorization")) + if auth != "" { + parts := strings.SplitN(auth, " ", 2) + if len(parts) == 2 && strings.EqualFold(parts[0], "Bearer") { + if k := strings.TrimSpace(parts[1]); k != "" { + return k + } } } - if v := strings.TrimSpace(c.GetHeader("x-api-key")); v != "" { - return v - } - if v := strings.TrimSpace(c.GetHeader("x-goog-api-key")); v != "" { - return v + + // 3) x-api-key header (backward compatibility) + if k := strings.TrimSpace(c.GetHeader("x-api-key")); k != "" { + return k } + + // 4) query parameter key (for specific paths) if allowGoogleQueryKey(c.Request.URL.Path) { if v := strings.TrimSpace(c.Query("key")); v != "" { return v } } + return "" }