Skip to content

fix: match auth header names case-insensitively (enables developer override) - #295

Merged
Tr00d merged 2 commits into
masterfrom
fix-developer-override
Aug 4, 2026
Merged

fix: match auth header names case-insensitively (enables developer override)#295
Tr00d merged 2 commits into
masterfrom
fix-developer-override

Conversation

@Tr00d

@Tr00d Tr00d commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

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() seeded apiKey into a case-sensitive Dictionary<string, string>, then merged the developer's Options.Headers on top. Because apiKey (SDK) and apikey (the Supabase convention a developer uses) are distinct keys to an ordinal dictionary, both survived and went on the wire as a duplicate, multi-valued apikey header.

The reporter used a header-based Storage RLS policy (request.headers ->> 'apikey'). With two apikey values, the policy matched neither, so From(bucket).List() returned an empty array with a 200 — a silent failure — while an equivalent raw HttpClient call sending a single apikey worked. ListBuckets() was unaffected because it doesn't depend on that policy.

The umbrella client already promises that developer-supplied headers take precedence (see the Authorization handling 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 with StringComparer.OrdinalIgnoreCase, so a developer header of any casing overwrites the SDK default (last-writer-wins, matching the existing merge order) rather than duplicating it.
  • Intent is expressed through a named CaseInsensitiveHeaders() factory rather than an explanatory comment.

Testing

  • New reproduction test SupabaseClient_ShouldPreferDeveloperApiKeyHeader_GivenApiKeyInOptions in SupabaseClientCompositionTests — the umbrella's own header-composition fixture, parallel to the existing Is it possible to pass in our own authorisation bearer token #5 Authorization test. It fails against the old code with {[apiKey, test-key], [apikey, developer-key]} and passes after the fix.
  • Inner loop green (33/33). The existing tests asserting ContainKey("apiKey") stay green — a case-insensitive lookup still finds the seeded key.

Compatibility

Non-breaking. GetAuthHeaders() is internal; 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

  • The scan stage of the quality gate flags two pre-existing transitive vulnerabilities in the Examples/BlazorWebAssemblySupabaseTemplate sample (EOL net7.0). They are unrelated to this change and left for a separate update.

Tr00d added 2 commits August 4, 2026 09:42
…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
@Tr00d
Tr00d merged commit ac057a2 into master Aug 4, 2026
1 check passed
@Tr00d
Tr00d deleted the fix-developer-override branch August 4, 2026 07:50
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.

FileList method fails ?silently?

1 participant