fix: match auth header names case-insensitively (enables developer override) - #295
Merged
Conversation
…erride) GetAuthHeaders seeded `apiKey` into a case-sensitive dictionary, so a developer-supplied `apikey` (the Supabase convention) was added as a separate entry instead of replacing it. Both went on the wire as a duplicate, multi-valued `apikey` header, so header-based Storage RLS saw the wrong value and List() returned empty with a 200. Build the header map with StringComparer.OrdinalIgnoreCase so any-cased developer header replaces the SDK default rather than coexisting with it. Fixes supabase-community/storage-csharp#19
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.
What
Auth headers supplied to child clients are now matched case-insensitively, so a developer-provided header (e.g.
apikey) replaces the SDK-seeded default instead of being sent alongside it.Fixes supabase-community/storage-csharp#19.
Why
Client.GetAuthHeaders()seededapiKeyinto a case-sensitiveDictionary<string, string>, then merged the developer'sOptions.Headerson top. BecauseapiKey(SDK) andapikey(the Supabase convention a developer uses) are distinct keys to an ordinal dictionary, both survived and went on the wire as a duplicate, multi-valuedapikeyheader.The reporter used a header-based Storage RLS policy (
request.headers ->> 'apikey'). With twoapikeyvalues, the policy matched neither, soFrom(bucket).List()returned an empty array with a 200 — a silent failure — while an equivalent rawHttpClientcall sending a singleapikeyworked.ListBuckets()was unaffected because it doesn't depend on that policy.The umbrella client already promises that developer-supplied headers take precedence (see the
Authorizationhandling for #5); this restores that guarantee for every header regardless of casing, which HTTP treats as case-insensitive anyway.Change
GetAuthHeaders()builds its header map withStringComparer.OrdinalIgnoreCase, so a developer header of any casing overwrites the SDK default (last-writer-wins, matching the existing merge order) rather than duplicating it.CaseInsensitiveHeaders()factory rather than an explanatory comment.Testing
SupabaseClient_ShouldPreferDeveloperApiKeyHeader_GivenApiKeyInOptionsinSupabaseClientCompositionTests— the umbrella's own header-composition fixture, parallel to the existing Is it possible to pass in our own authorisation bearer token #5Authorizationtest. It fails against the old code with{[apiKey, test-key], [apikey, developer-key]}and passes after the fix.ContainKey("apiKey")stay green — a case-insensitive lookup still finds the seeded key.Compatibility
Non-breaking.
GetAuthHeaders()isinternal; the only observable difference is that the returned header map is now case-insensitive. No public API, serialized shape, or default behavior changes for callers who don't override a header.Notes
Examples/BlazorWebAssemblySupabaseTemplatesample (EOLnet7.0). They are unrelated to this change and left for a separate update.