Skip to content

feat(auth): accept x-api-key header so Anthropic-native clients work (fixes #253) - #315

Merged
njbrake merged 5 commits into
mozilla-ai:mainfrom
AloysJehwin:feat/x-api-key-auth
Jul 20, 2026
Merged

feat(auth): accept x-api-key header so Anthropic-native clients work (fixes #253)#315
njbrake merged 5 commits into
mozilla-ai:mainfrom
AloysJehwin:feat/x-api-key-auth

Conversation

@AloysJehwin

@AloysJehwin AloysJehwin commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Summary

Anthropic SDK clients configured with ANTHROPIC_API_KEY send credentials as a bare x-api-key header (no Bearer prefix). Against otari these requests got a 401 on every route, making x-api-key the only thing standing between an Anthropic-native client and a working setup.

This PR adds x-api-key as a final fallback in _extract_bearer_token, after all existing header checks.

PR Type

  • New Feature
  • Bug Fix
  • Refactor
  • Documentation
  • Infrastructure / CI

Relevant issues

Fixes #253

Changes

  • config.py — adds X_API_KEY_HEADER = "x-api-key" constant
  • deps.py_extract_bearer_token falls back to x-api-key (raw token, no Bearer prefix) before raising 401; Authorization: Bearer wins if both are present
  • main.pyx-api-key added to CORS allow_headers; XApiKeyAuth OpenAPI security scheme added alongside ApiKeyAuth
  • tests/unit/test_extract_bearer_token.py — 4 new cases: raw token extraction, Authorization precedence over x-api-key, canonical Otari-Key precedence, no-Bearer-prefix succeeds

Precedence (unchanged headers win)

Otari-Key > AnyLLM-Key/X-AnyLLM-Key > Authorization: Bearer > x-api-key

Decisions from issue #253

  1. Scope — global across all routes (smaller diff, consistent behaviour)
  2. Token format — raw token branch added separately from the Bearer path; no prefix stripping needed
  3. PrecedenceAuthorization wins over x-api-key if both are present

Checklist

  • lint (make lint) passes
  • typecheck (make typecheck) passes — 219 files clean
  • 14/14 unit tests pass (tests/unit/test_extract_bearer_token.py)

AI Usage

Drafted with Claude Code assistance; logic and test cases reviewed manually.

Summary

  • Added support for authenticating with the x-api-key header (for Anthropic-native clients), including a new shared constant for the header name.
  • Kept the existing authentication order, now with x-api-key used as the final fallback (after Otari-Key, AnyLLM-Key/X-AnyLLM-Key, and Authorization: Bearer).
  • Updated browser/CORS settings and OpenAPI security documentation to recognize x-api-key.
  • Added unit tests to verify correct token extraction, precedence rules, and handling of raw (non-Bearer-prefixed) values, using obviously fake tokens to avoid false positives.

Anthropic SDK clients send credentials as a bare x-api-key header with no
Bearer prefix. Previously _extract_bearer_token only read Otari-Key,
AnyLLM-Key/X-AnyLLM-Key aliases, and Authorization: Bearer <token>, so those
requests got a 401 at the on-ramp.

Changes:
- Add X_API_KEY_HEADER = "x-api-key" constant in config.py
- _extract_bearer_token falls back to x-api-key (raw, no Bearer prefix) after
  all existing header checks, before raising 401 — Authorization wins if both
  are present
- CORS allow_headers includes x-api-key for browser clients
- OpenAPI spec adds XApiKeyAuth security scheme alongside ApiKeyAuth
- 4 new tests in test_extract_bearer_token.py covering raw token extraction,
  precedence over x-api-key, and no-Bearer-prefix path

Closes mozilla-ai#253

Signed-off-by: Aloys Jehwin <aloysjehwin@gmail.com>
@github-actions github-actions Bot added the missing-template PR is missing required template sections label Jul 18, 2026
@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

The gateway adds x-api-key authentication support, preserving existing header precedence, documenting the new security scheme in OpenAPI, allowing the header through CORS, and testing raw-token extraction behavior.

Changes

x-api-key authentication

Layer / File(s) Summary
Credential extraction and validation
src/gateway/core/config.py, src/gateway/api/deps.py, tests/unit/test_extract_bearer_token.py
Adds the x-api-key header constant, extracts its raw value, preserves existing precedence rules, and tests prefixed and unprefixed values.
API documentation and CORS wiring
src/gateway/main.py
Adds the XApiKeyAuth OpenAPI scheme, accepts it for non-public routes, and permits x-api-key in CORS requests.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related issues

Possibly related PRs

Suggested reviewers: peteski22

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ⚠️ Warning The title is relevant, but it misses the required plain prefix rule and exceeds the ~70 character limit. Use a short Conventional Commit title like feat: accept x-api-key header and move issue details to the PR body.
✅ Passed checks (3 passed)
Check name Status Explanation
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 The description is mostly complete and covers summary, issue, scope, tests, and AI use, with only minor template-field gaps.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
✨ Simplify code
  • Create PR with simplified code

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai
coderabbitai Bot requested a review from peteski22 July 18, 2026 17:16
Avoids GitGuardian false-positive on sk-ant-api03- prefix pattern.

@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.

🧹 Nitpick comments (1)
src/gateway/api/deps.py (1)

100-108: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Update the error message to include the new header.

Hi there! Great work adding the fallback for Anthropic clients. Just a small detail: since we now support the x-api-key header, we should probably mention it in this 401 error message so that clients know it's a valid authentication option. As per coding guidelines, we should raise explicit HTTPException instances with clear detail messages.

✨ Proposed tweak
     raw_token = request.headers.get(X_API_KEY_HEADER)
     if raw_token:
         return raw_token
 
     record_auth_failure("missing_credentials")
     raise HTTPException(
         status_code=status.HTTP_401_UNAUTHORIZED,
-        detail=f"Missing {API_KEY_HEADER} or Authorization header",
+        detail=f"Missing {API_KEY_HEADER}, Authorization, or {X_API_KEY_HEADER} header",
     )
🤖 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 `@src/gateway/api/deps.py` around lines 100 - 108, Update the HTTPException
detail in the authentication fallback around raw_token to mention
X_API_KEY_HEADER alongside API_KEY_HEADER and Authorization, while preserving
the existing 401 status and failure-recording behavior.

Source: Coding guidelines

🤖 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.

Nitpick comments:
In `@src/gateway/api/deps.py`:
- Around line 100-108: Update the HTTPException detail in the authentication
fallback around raw_token to mention X_API_KEY_HEADER alongside API_KEY_HEADER
and Authorization, while preserving the existing 401 status and
failure-recording behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 532bdbca-8af1-4195-a4e4-45d9ad6d56dd

📥 Commits

Reviewing files that changed from the base of the PR and between 0c3542b and 4adad0f.

📒 Files selected for processing (4)
  • src/gateway/api/deps.py
  • src/gateway/core/config.py
  • src/gateway/main.py
  • tests/unit/test_extract_bearer_token.py

Regenerate docs/public/openapi.json to include the XApiKeyAuth security
scheme (make openapi-check was failing on the stale artifact), mention
x-api-key in the missing-credentials 401 detail, and note the fallback
in the _extract_bearer_token docstring.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@njbrake
njbrake temporarily deployed to integration-tests July 20, 2026 09:52 — with GitHub Actions Inactive
@codecov-commenter

codecov-commenter commented Jul 20, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 75.00000% with 2 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/gateway/main.py 33.33% 2 Missing ⚠️
Files with missing lines Coverage Δ
src/gateway/api/deps.py 95.96% <100.00%> (ø)
src/gateway/core/config.py 94.90% <100.00%> (ø)
src/gateway/main.py 85.89% <33.33%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

njbrake and others added 2 commits July 20, 2026 10:02
Bring in mozilla-ai#297 (runtime provider management) so the generated OpenAPI spec
reflects both the provider-credentials endpoints and this PR's x-api-key
security scheme.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Regenerate docs/public/openapi.json from the merged code so it includes
both the provider-credentials endpoints from mozilla-ai#297 and this PR's
XApiKeyAuth security scheme.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@njbrake
njbrake temporarily deployed to integration-tests July 20, 2026 10:02 — with GitHub Actions Inactive

@njbrake njbrake left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Note: this review was drafted by Claude via back-and-forth with @njbrake. The reasoning and decisions are his; the prose is Claude's.

Approving

The x-api-key fallback is correct and well scoped. It solves the real interoperability gap from #253 (Anthropic-native clients send a bare x-api-key and were getting a 401 on every route), with no security regression: the raw token flows through the same hash and lookup path as the Bearer credential. Precedence is Otari-Key then legacy AnyLLM-Key / X-AnyLLM-Key then Authorization: Bearer then x-api-key, verified against the source and the new tests.

What was folded in on top of the original change:

  • Regenerated docs/public/openapi.json so the XApiKeyAuth security scheme is in the committed artifact (make openapi-check was failing on the stale spec).
  • Added x-api-key to the missing-credentials 401 detail message and to the _extract_bearer_token docstring.
  • Merged current main into the branch and regenerated the spec, so it now reflects both the provider-credentials endpoints from #297 and this PR's security scheme. Without that, CI (which builds the PR merged with main) failed the OpenAPI freshness check.

CI is green: lint, typecheck, tests, the OpenAPI freshness job, and SDK codegen all pass.

One housekeeping item that is not a merge blocker: the PR description is missing the ## PR Type section, which keeps the PR Template Check red and arms the daily auto-close job. Adding that section clears the label and the pending close.

@github-actions github-actions Bot removed the missing-template PR is missing required template sections label Jul 20, 2026
@njbrake
njbrake merged commit 4b4bd96 into mozilla-ai:main Jul 20, 2026
12 checks passed
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.

feat(auth): accept x-api-key so Anthropic-native clients work on /v1/messages

3 participants