fix: revert explicit apikey header (caused Kong duplicate-apikey 401)
Some checks failed
api-ci-deploy / test-build-deploy (push) Has been cancelled

The previous commit added apikey to _create_base_client headers, but supabase-py
already sets apikey from the key arg → two apikey headers → Kong rejected every
as-user call with 401 'Duplicate API key found' (exam API 502'd on auth). Revert
to Authorization-only; fix the two header unit tests to assert the real contract
(apikey via the key arg; options.headers carries only the user Authorization).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
CC Worker 2026-06-06 19:30:36 +00:00
parent f3da9f3b59
commit 93972a62f7
3 changed files with 11 additions and 10 deletions

View File

@ -24,12 +24,11 @@ def _create_base_client(url: str, key: str, access_token: Optional[str] = None,
# Otherwise fall back to the API key
auth_header = f"Bearer {access_token}" if access_token else f"Bearer {key}"
# apikey is required by the Supabase gateway (Kong) on every request and is independent of
# Authorization: for a per-user client apikey stays the anon key while Authorization carries
# the user's JWT (so RLS sees auth.uid()). Set it explicitly rather than relying on
# create_client's internal default-header behaviour, which our options.headers override.
# Only override Authorization here. apikey is supplied to create_client via the `key` arg and
# set by supabase-py itself; setting it again here sends a DUPLICATE apikey header that the
# Supabase gateway (Kong) rejects with 401 "Duplicate API key found". For a per-user client
# apikey stays the anon key (from `key`) while this Authorization carries the user JWT.
headers = {
"apikey": key,
"Authorization": auth_header,
}
if options:

View File

@ -122,11 +122,11 @@ def test_supabase_client_for_user_uses_access_token_authorization(monkeypatch):
assert anon.access_token == "user-token"
assert captured["url"] == "http://supabase.test"
# apikey is supplied via the `key` positional arg (supabase-py sets the apikey header from it).
# options.headers must carry ONLY the per-user Authorization override — adding apikey here too
# produces a duplicate apikey header that Kong rejects ("Duplicate API key found").
assert captured["key"] == "anon-key"
assert captured["options_kwargs"]["headers"] == {
"apikey": "anon-key",
"Authorization": "Bearer user-token",
}
assert captured["options_kwargs"]["headers"] == {"Authorization": "Bearer user-token"}
def test_no_school_bootstrap_requires_school_membership_but_allows_canvas():

View File

@ -26,8 +26,10 @@ def test_supabase_anon_for_user_sets_user_authorization_header(monkeypatch):
client_module.SupabaseAnonClient.for_user('Bearer user-jwt')
# apikey comes from the `key` arg (supabase-py sets the apikey header); options.headers must
# carry only the user Authorization override. A second apikey here → Kong "Duplicate API key".
assert captured['key'] == 'anon-key'
assert captured['options'].headers['apikey'] == 'anon-key'
assert 'apikey' not in captured['options'].headers
assert captured['options'].headers['Authorization'] == 'Bearer user-jwt'