Skip to content

fix(security): gate /v1beta endpoints behind api_keys auth#46

Open
Leslielu wants to merge 1 commit into
Sophomoresty:mainfrom
Leslielu:fix/v1beta-auth-gate
Open

fix(security): gate /v1beta endpoints behind api_keys auth#46
Leslielu wants to merge 1 commit into
Sophomoresty:mainfrom
Leslielu:fix/v1beta-auth-gate

Conversation

@Leslielu

@Leslielu Leslielu commented Jul 1, 2026

Copy link
Copy Markdown

Problem

The api_keys auth gate only matched self.path.startswith("/v1/"). The Google-native /v1beta/* endpoints (Gemini CLI: /v1beta/models/{model}:generateContent and :streamGenerateContent) don't start with /v1/, so they bypassed _authorized() entirely. Any request — including ?key=test — was forwarded straight to the Gemini upstream.

Impact

On a publicly exposed instance, anyone can abuse /v1beta with an arbitrary key to proxy Gemini through your host, draining your egress IP's quota and triggering 429 Too Many Requests for all legitimate users. (Observed in the wild: a host received continuous ?key=test traffic on /v1beta and got rate-limited by Gemini for days.)

Fix

  • Widen the gate from startswith("/v1/") to startswith("/v1") so /v1beta is covered (do_GET + do_POST).
  • _authorized() now also accepts x-goog-api-key header and ?key= query param (the native styles Gemini CLI sends), so legitimate CLI usage keeps working under auth.
  • Fixed a latent bug: the old ternary short-circuited on a present-but-wrong Bearer header and never fell back to x-api-key; now every key source is checked.
  • Applied to both the monolithic gemini_web2api.py and the package gemini_web2api/server.py.

Compatibility

Gemini CLI users must set GEMINI_API_KEY to a real key from api_keys (any placeholder worked before only because /v1beta was unauthenticated).

Verification

request before after
/v1beta/models?key=test 200 (forwarded) 401
/v1beta/models (no key) 200 (forwarded) 401
/v1beta/models?key=<valid> 200 200
/v1/models wrong Bearer 401 401
/v1/models valid Bearer 200 200

🤖 Generated with Claude Code

The auth gate only matched startswith("/v1/"), so the Google-native
/v1beta/* endpoints (Gemini CLI) bypassed api_keys entirely. Any request,
including ?key=test, was forwarded straight to the Gemini upstream, draining
the host IP's quota and triggering 429 rate limits for legitimate users.

- Widen gate from startswith("/v1/") to startswith("/v1") so /v1beta is covered
- _authorized() now also accepts x-goog-api-key header and ?key= query param
  (Gemini CLI native styles), and checks every key source instead of
  short-circuiting on a present-but-wrong Bearer header
- Applied to both monolithic gemini_web2api.py and package gemini_web2api/server.py

Co-Authored-By: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant