Skip to content

fix(client): prevent duplicate authentication headers - #360

Open
elyasmnvidian wants to merge 1 commit into
mainfrom
emehtabuddin/fix-reserved-extra-headers
Open

fix(client): prevent duplicate authentication headers#360
elyasmnvidian wants to merge 1 commit into
mainfrom
emehtabuddin/fix-reserved-extra-headers

Conversation

@elyasmnvidian

@elyasmnvidian elyasmnvidian commented Aug 11, 2026

Copy link
Copy Markdown
Contributor
schema_version = 1

[llm_clients.capture]
format = "openai_chat"
base_url = "https://example.test/v1"
api_key_env = "MODEL_SERVER_API_KEY"
extra_headers = { Authorization = "Bearer extra-key", X-Inference-Priority = "batch" }

[targets.primary]
id = "example/model"
llm_client = "capture"

[routes.direct]
id = "direct"
type = "passthrough"
target = "primary"

Problem

This config creates an authentication conflict: the API key loaded from api_key_env and extra_headers.Authorization both define the outgoing Authorization header. Switchyard appends both values instead of choosing one. The NVIDIA Inference Hub gateway combines the duplicate values into one comma-separated string and checks that string as one token. It therefore rejects every request with 401 even though the API key loaded from api_key_env is valid.

authorization: Bearer extra-key
authorization: Bearer configured-key
model server reads: Bearer extra-key, Bearer configured-key
HTTP/1.1 401 Unauthorized

Fix

Switchyard now resolves the conflict in favor of the configured API key. For OpenAI requests, the API key configured through api_key or loaded from api_key_env wins over extra_headers.Authorization; Switchyard sends the configured key once and does not send the conflicting custom value. For Anthropic requests, the configured API key wins over extra_headers.x-api-key, and Switchyard continues to set the required anthropic-version value.

Without a configured API key, there is no conflict, so Switchyard still sends the custom Authorization or x-api-key header. Other custom headers are unchanged. Header names are matched without regard to letter case.

Proof

A local HTTP server captured the headers sent after loading the config above:

POST /v1/chat/completions HTTP/1.1
x-inference-priority: batch
authorization: Bearer configured-key

The request contained one Authorization header and returned a successful response for example/model.

Compatibility: Configs that set authentication only through extra_headers keep working. When the configured API key and custom authentication header conflict, Switchyard omits the custom value.

Testing

cargo test -p switchyard-llm-client -p switchyard-server passed 113 tests. The regression test sends OpenAI and Anthropic requests to a local HTTP server and verifies both cases: the configured API key wins when the settings conflict, and the custom authentication header remains when no API key is configured.

@elyasmnvidian
elyasmnvidian requested a review from a team as a code owner August 11, 2026 17:24
@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown
PR Preview Action v1.8.1

🚀 View preview at
https://NVIDIA-NeMo.github.io/Switchyard/pr-preview/pr-360/

Built to branch gh-pages at 2026-08-12 17:44 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

@elyasmnvidian
elyasmnvidian force-pushed the emehtabuddin/fix-reserved-extra-headers branch from 8ea93f5 to 00b724c Compare August 11, 2026 17:26
@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

The change rejects case-insensitive conflicts with backend-owned headers in LLM client, native backend, Python, and route construction paths. It adds validation coverage and updates configuration documentation.

Changes

Reserved header validation

Layer / File(s) Summary
Validation contract and error handling
crates/libsy-llm-client/src/backend.rs, crates/switchyard-components/src/contracts/backend.rs
Backend and target validation reject Authorization, x-api-key, and anthropic-version conflicts case-insensitively.
Construction-time enforcement
crates/libsy-llm-client/src/client.rs, crates/switchyard-components/src/backends/*.rs, crates/switchyard-py/src/component_bindings/config.rs
Client, native backend, and Python target construction validate headers before initialization completes.
Tests and configuration documentation
crates/switchyard-components/tests/adversarial_native_backends.rs, crates/switchyard-server/src/config.rs, tests/test_route_bundle.py, crates/libsy-llm-client/README.md, docs/reference/toml_schema.md
Tests cover reserved-header rejection and valid custom headers. Documentation describes the restriction and credential configuration.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Poem

A rabbit checks each header’s name,
No backend secret may be claimed.
Mixed-case tricks now fall away,
Custom headers hop safely through the day.
The config garden blooms in tune.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: preventing duplicate backend-owned authentication headers through configuration validation.

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/switchyard-components/src/contracts/backend.rs`:
- Around line 104-108: Update the documentation comment describing custom
headers to state that validation prevents duplicate backend-owned headers,
including case-insensitive rejection of Authorization, x-api-key, and
anthropic-version names. Remove the claim that every outbound request carries
exactly one authentication and protocol header, and do not imply OpenAI targets
send anthropic-version.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: e05bfeb2-145b-455c-9ba3-4fe7397a0dc0

📥 Commits

Reviewing files that changed from the base of the PR and between 6df31a9 and 00b724c.

📒 Files selected for processing (11)
  • crates/libsy-llm-client/README.md
  • crates/libsy-llm-client/src/backend.rs
  • crates/libsy-llm-client/src/client.rs
  • crates/switchyard-components/src/backends/anthropic.rs
  • crates/switchyard-components/src/backends/openai.rs
  • crates/switchyard-components/src/contracts/backend.rs
  • crates/switchyard-components/tests/adversarial_native_backends.rs
  • crates/switchyard-py/src/component_bindings/config.rs
  • crates/switchyard-server/src/config.rs
  • docs/reference/toml_schema.md
  • tests/test_route_bundle.py

Comment on lines +104 to +108
/// Custom headers are appended to the backend's standard headers.
/// Backend-owned ``Authorization``, ``x-api-key``, and
/// ``anthropic-version`` names are rejected case-insensitively so
/// each outbound request carries exactly one authentication and
/// protocol header.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Correct the outbound-header guarantee.

OpenAI targets do not send anthropic-version. State that validation prevents duplicate backend-owned headers. Do not state that every request carries one authentication and protocol header.

Proposed documentation fix
-    /// each outbound request carries exactly one authentication and
-    /// protocol header.
+    /// This prevents duplicate backend-owned authentication and protocol
+    /// headers on outbound requests.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/// Custom headers are appended to the backend's standard headers.
/// Backend-owned ``Authorization``, ``x-api-key``, and
/// ``anthropic-version`` names are rejected case-insensitively so
/// each outbound request carries exactly one authentication and
/// protocol header.
/// Custom headers are appended to the backend's standard headers.
/// Backend-owned ``Authorization``, ``x-api-key``, and
/// ``anthropic-version`` names are rejected case-insensitively so
/// This prevents duplicate backend-owned authentication and protocol
/// headers on outbound requests.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/switchyard-components/src/contracts/backend.rs` around lines 104 -
108, Update the documentation comment describing custom headers to state that
validation prevents duplicate backend-owned headers, including case-insensitive
rejection of Authorization, x-api-key, and anthropic-version names. Remove the
claim that every outbound request carries exactly one authentication and
protocol header, and do not imply OpenAI targets send anthropic-version.

@elyasmnvidian
elyasmnvidian force-pushed the emehtabuddin/fix-reserved-extra-headers branch 2 times, most recently from 37ccc8d to 5972a8a Compare August 11, 2026 20:52
@elyasmnvidian elyasmnvidian changed the title fix(config): reject backend-owned extra headers fix(client): keep backend auth authoritative Aug 11, 2026
@elyasmnvidian
elyasmnvidian force-pushed the emehtabuddin/fix-reserved-extra-headers branch from 5972a8a to 50ffc2d Compare August 11, 2026 22:06
@elyasmnvidian elyasmnvidian changed the title fix(client): keep backend auth authoritative fix(client): prevent duplicate authentication headers Aug 11, 2026
@elyasmnvidian
elyasmnvidian force-pushed the emehtabuddin/fix-reserved-extra-headers branch from 50ffc2d to 5e26ad8 Compare August 12, 2026 16:37
Signed-off-by: Elyas Mehtabuddin <emehtabuddin@nvidia.com>
@elyasmnvidian
elyasmnvidian force-pushed the emehtabuddin/fix-reserved-extra-headers branch from 5e26ad8 to 5cf5661 Compare August 12, 2026 17:43
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