Conversation
`GoTrueBaseAPI.__init__` stored the caller's `headers` dict by reference, and `GoTrueClient.__init__` hands the admin API its own `_headers`. That made `client._headers is client.admin._headers`. Anything that then rewrote the client's `Authorization` header in place also rewrote the admin one. In the `supabase` wrapper, `_listen_to_auth_events` does exactly that, so after `sign_in_with_password()` every `auth.admin.*` call ran with the signed-in user's token instead of the service key and failed with "User not allowed". Copying the dict on the way in fixes it for every consumer, including code that builds `GoTrueClient` or `GoTrueAdminAPI` directly, and makes in-place header mutation safe by construction. Fixes #1404
Contributor
|
The following capabilities are marked
The following capabilities are marked
These may have been renamed, removed, or never registered. Please update the capability matrix. |
Tr00d
approved these changes
Sep 21, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Supersedes #1406. Same bug, fixed one layer down. Credit to @AdityaMedidala for finding and diagnosing it in #1404 / #1406.
The bug
GoTrueBaseAPI.__init__stored the caller's dict by reference:and
GoTrueClient.__init__hands the admin API its own_headers:So
client._headers is client.admin._headers. Any in-place rewrite of the client'sAuthorizationheader rewrote the admin one too.The
supabasewrapper does exactly that on every auth event:Result: a client built with the
service_rolekey loses admin rights the moment a user signs in.Reproduced against a local Supabase instance
After this change:
Why here and not in the wrapper
#1406 fixes it in
supabase/_async/client.pyby copying the admin dict and re-asserting the key on every auth event. That works, but it leaves the sharp edge in place: anyone constructingGoTrueClientorGoTrueAdminAPIdirectly still gets the aliased dict, and any future in-place header write reintroduces the bug.Copying on the way in is one line, needs no wrapper changes at all, and makes in-place mutation safe by construction. I verified the wrapper needs no edit:
_listen_to_auth_eventsis the only in-place_headersmutation in either package, and the wrapper already updatesoptions.headers, postgrest and storage explicitly, so nothing relied on the sharing.Tests
Four new tests in
tests/_async/test_admin_headers.py, generated to_sync. Both cover the changed line: the client/admin aliasing, and a caller that keeps mutating the dict it passed in. All four fail without the fix and pass with it.Verification
Every package, locally, against real infra (gotrue v2.187, PostgREST v11.2.2 and v16.2, storage-api v1.72.1):
ruff checkandruff format --checkboth clean._syncregenerated withmake auth.build-sync.