Skip to content

Conversation

@ZiuChen
Copy link

@ZiuChen ZiuChen commented Nov 7, 2025

Add API Key Authentication Support

🎯 Overview

This pull request adds comprehensive API key authentication support to the Copilot API proxy, allowing administrators to secure their proxy endpoints with configurable API keys.

✨ Features

🔐 Flexible Authentication

  • Multiple Authentication Formats: Supports both OpenAI (Authorization: Bearer <token>) and Anthropic (x-api-key: <token>) authentication formats
  • Multiple API Keys: Configure multiple API keys to support different clients or environments
  • Selective Protection: Only API endpoints require authentication; the root endpoint remains publicly accessible

🛠️ Configuration

  • CLI Integration: New --api-key flag supports multiple values for easy configuration
  • Runtime Configuration: API keys are stored in application state and can be specified at startup

🔒 Security Features

  • 401 Unauthorized: Proper error responses for missing or invalid API keys
  • Comprehensive Coverage: All API endpoints (/v1/chat/completions, /v1/models, /v1/embeddings, /v1/messages, /usage, /token) are protected when API keys are configured

📝 Changes

New Files

  • api-key-auth.ts: Core authentication middleware with support for both OpenAI and Anthropic authentication formats

Modified Files

  • state.ts: Added apiKeys field to application state interface
  • server.ts: Integrated authentication middleware for all protected endpoints
  • start.ts: Added CLI option handling and initialization logic for API keys
  • README.md: Comprehensive documentation including configuration examples and usage patterns

🚀 Usage Examples

Single API Key

npx copilot-api@latest start --api-key your_secret_key

Multiple API Keys

npx copilot-api@latest start --api-key key1 --api-key key2 --api-key key3

Authentication Requests

# OpenAI format
curl -H "Authorization: Bearer your_api_key_here" http://localhost:4141/v1/models

# Anthropic format  
curl -H "x-api-key: your_api_key_here" http://localhost:4141/v1/messages

💡 Implementation Notes

  • Backward Compatibility: When no API keys are configured, the proxy behaves exactly as before
  • Performance: Lightweight middleware with minimal overhead
  • Extensibility: Clean architecture allows for easy addition of more authentication methods

Summary by CodeRabbit

  • New Features

    • Added API key authentication with a new --api-key option supporting single or multiple keys
    • Accepts keys via Bearer token or x-api-key header
    • When keys are configured, most endpoints require authentication; root endpoint remains public
    • Invalid or missing keys return 401 responses
  • Documentation

    • Added API Key Authentication docs and updated usage/admin examples showing single and multiple key configurations

@coderabbitai
Copy link

coderabbitai bot commented Nov 10, 2025

Walkthrough

Adds API key authentication: new middleware extracts keys from Authorization Bearer and x-api-key header; --api-key CLI option configures one or more keys and populates state.apiKeys; middleware enforces auth on protected routes and returns 401 for missing/invalid keys.

Changes

Cohort / File(s) Summary
Documentation
README.md
Added "API Key Authentication" section, examples for enabling single/multiple keys via --api-key, and notes about protected endpoints and 401 responses.
Auth middleware
src/lib/api-key-auth.ts
New middleware apiKeyAuthMiddleware that extracts API key from Authorization: Bearer and x-api-key header, validates against state.apiKeys using constant-time compare, skips auth when no keys configured, returns 401 with descriptive messages.
State interface
src/lib/state.ts
State interface extended with optional apiKeys?: Array<string>.
Server routing
src/server.ts
Imported and registered apiKeyAuthMiddleware on protected endpoints (models, embeddings, usage, token, messages and v1 equivalents).
CLI / startup
src/start.ts
Added --api-key CLI option and parsing to normalize single or multiple keys into apiKeys array; passed into runServer and stored in state.apiKeys; logs when API key authentication is enabled.
Utilities
src/lib/utils.ts
Added constantTimeEqual(a: string, b: string): boolean for constant-time string comparisons used by the auth middleware.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Server
    participant Auth as API Key Auth Middleware
    participant Handler as Route Handler

    Client->>Server: HTTP Request
    Server->>Auth: Invoke middleware
    alt state.apiKeys configured
        Auth->>Auth: Extract key (Authorization Bearer / x-api-key)
        alt key valid
            Auth->>Handler: forward request
            Handler->>Client: 200 OK
        else invalid/missing
            Auth->>Client: 401 Unauthorized
        end
    else no apiKeys configured
        Auth->>Handler: skip auth
        Handler->>Client: 200 OK
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Review src/lib/api-key-auth.ts for header parsing edge cases and exact 401 message wording.
  • Verify constantTimeEqual in src/lib/utils.ts handles Unicode/codepoint comparisons correctly.
  • Confirm src/start.ts CLI parsing correctly normalizes single string vs. array inputs and handles empty values.
  • Check src/server.ts middleware ordering and that only intended routes are protected.

Poem

🐇 I sniffed the Bearer and the x-api-key,
Stored a bundle of secrets safe away,
I stand at routes both near and far,
Guarding endpoints like a tiny star,
Hopping keys into the night’s soft gray.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely summarizes the main feature being added: support for specifying multiple API keys. It aligns with the primary changeset objectives.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fb889b9 and c1d0139.

📒 Files selected for processing (1)
  • src/lib/api-key-auth.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/lib/api-key-auth.ts

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

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/lib/api-key-auth.ts (1)

37-65: Consider constant-time comparison for API key validation.

The middleware logic is well-structured and correctly handles missing and invalid keys. However, line 55 uses Array.includes() for key validation, which is not constant-time and could theoretically be vulnerable to timing attacks.

For enhanced security, consider using a constant-time comparison:

// Helper function for constant-time string comparison
function constantTimeEqual(a: string, b: string): boolean {
  if (a.length !== b.length) {
    return false
  }
  let result = 0
  for (let i = 0; i < a.length; i++) {
    result |= a.charCodeAt(i) ^ b.charCodeAt(i)
  }
  return result === 0
}

// Then in the middleware:
const isValidKey = state.apiKeys.some(key => constantTimeEqual(key, providedKey))

Note: For typical API keys (UUIDs or long random strings), the timing attack risk is low, so this is an optional enhancement rather than a critical fix.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0ea08fe and 666a0ec.

📒 Files selected for processing (5)
  • README.md (3 hunks)
  • src/lib/api-key-auth.ts (1 hunks)
  • src/lib/state.ts (1 hunks)
  • src/server.ts (2 hunks)
  • src/start.ts (4 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
src/lib/api-key-auth.ts (1)
src/lib/state.ts (1)
  • state (23-28)
src/server.ts (1)
src/lib/api-key-auth.ts (1)
  • apiKeyAuthMiddleware (37-65)
src/start.ts (1)
src/lib/state.ts (1)
  • state (23-28)
🪛 LanguageTool
README.md

[style] ~166-~166: To form a complete sentence, be sure to include a subject.
Context: ...key | API keys for authentication. Can be specified multiple times ...

(MISSING_IT_THERE)

🔇 Additional comments (7)
src/lib/state.ts (1)

18-20: LGTM!

The addition of the optional apiKeys property to the State interface is clean and maintains backward compatibility when no API keys are configured.

src/server.ts (1)

5-26: LGTM! Middleware integration is correct.

The apiKeyAuthMiddleware is properly applied to all protected endpoints. The root endpoint remains public as intended, and the middleware coverage correctly includes both standard and v1-prefixed routes.

README.md (1)

277-281: LGTM! Clear usage examples.

The examples effectively demonstrate how to use the --api-key flag with both single and multiple keys.

src/start.ts (3)

28-28: LGTM! Options interface updated correctly.

The addition of the optional apiKeys property to RunServerOptions is consistent with the backward-compatible design.


50-56: LGTM! Clear initialization and logging.

The state initialization and informational logging provide good user feedback about the authentication status.


195-211: LGTM! Robust CLI argument parsing.

The implementation correctly handles citty's behavior of passing either a string (single value) or array (multiple values), ensuring the --api-key flag works as documented.

src/lib/api-key-auth.ts (1)

11-31: Verified: Query parameter API key support is an intentional trade-off to enable browser-based Usage Viewer access.

The security concern is legitimate. The /usage endpoint is protected by apiKeyAuthMiddleware (src/server.ts line 21), which intentionally supports query parameters (src/lib/api-key-auth.ts comment: "for extra compatibility of /usage or /token route"). This design enables the browser-based Usage Viewer documented in the README to fetch usage data from localhost, since external browser viewers cannot set custom headers due to CORS restrictions.

However, this creates the security risks you identified:

  • API keys appear in server logs and browser history
  • Keys leak via Referer headers when navigating externally
  • URL bar exposes credentials

Before committing to a mitigation approach, verify:

  • Is the Usage Viewer expected to work with API key authentication enabled?
  • Are there existing security controls (firewall, network isolation) limiting access to /usage?
  • Is this endpoint intended to be public or accessed only from specific networks?

If the browser viewer must support API key auth, your three suggestions remain valid. If not, removing query parameter support eliminates the security gap.

Comment on lines +213 to +246
## API Key Authentication

The proxy supports API key authentication to restrict access to the endpoints. When API keys are configured, all API endpoints require authentication.

### Authentication Methods

The proxy supports both OpenAI and Anthropic authentication formats:

- **OpenAI format**: Include the API key in the `Authorization` header with `Bearer` prefix:
```bash
curl -H "Authorization: Bearer your_api_key_here" http://localhost:4141/v1/models
```

- **Anthropic format**: Include the API key in the `x-api-key` header:
```bash
curl -H "x-api-key: your_api_key_here" http://localhost:4141/v1/messages
```

### Configuration

Use the `--api-key` flag to enable API key authentication. You can specify multiple keys for different clients:

```bash
# Single API key
npx copilot-api@latest start --api-key your_secret_key

# Multiple API keys
npx copilot-api@latest start --api-key key1 --api-key key2 --api-key key3
```

When API keys are configured:
- All API endpoints (`/v1/chat/completions`, `/v1/models`, `/v1/embeddings`, `/v1/messages`, `/usage`, `/token`) require authentication
- Requests without valid API keys will receive a 401 Unauthorized response
- The root endpoint `/` remains accessible without authentication
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Document usage viewer behavior with API key authentication.

The API Key Authentication section is well-written and comprehensive. However, there's a documentation gap: the Usage Viewer section (lines 302-322) doesn't explain how to access the dashboard when API key authentication is enabled. Users may be confused about whether they need to provide an API key in the URL or if the endpoint remains accessible.

Consider adding a note in either the API Key Authentication section or the Usage Viewer section explaining how authentication works with the browser-based dashboard (e.g., whether the /usage endpoint requires authentication when accessed directly vs. through the dashboard).

🤖 Prompt for AI Agents
In README.md around lines 213-246, add a short note clarifying how the Usage
Viewer/dashboard is affected when API key authentication is enabled: inspect the
server implementation to determine whether the /usage endpoint and the browser
dashboard require the API key, and then document the exact way to authenticate
when using the dashboard (e.g., which header/query param/cookie or UI login flow
the dashboard accepts), including an example of how to provide the key from a
browser (or mention browser extensions/tools if direct header setting is not
supported).

@ZiuChen
Copy link
Author

ZiuChen commented Nov 10, 2025

@coderabbitai generate docstrings

@coderabbitai
Copy link

coderabbitai bot commented Nov 10, 2025

✅ Actions performed

Initiated docstring generation; will generate only if new commits exist.

@coderabbitai
Copy link

coderabbitai bot commented Nov 10, 2025

Note

Docstrings generation - SUCCESS
Generated docstrings for this pull request at #146

coderabbitai bot added a commit that referenced this pull request Nov 10, 2025
Docstrings generation was requested by @ZiuChen.

* #144 (comment)

The following files were modified:

* `src/lib/api-key-auth.ts`
* `src/start.ts`
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 666a0ec and fb889b9.

📒 Files selected for processing (2)
  • src/lib/api-key-auth.ts (1 hunks)
  • src/lib/utils.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/lib/api-key-auth.ts (2)
src/lib/state.ts (1)
  • state (23-28)
src/lib/utils.ts (1)
  • constantTimeEqual (28-37)
🔇 Additional comments (2)
src/lib/utils.ts (1)

27-37: LGTM! Solid constant-time comparison implementation.

The function correctly prevents timing attacks by using XOR accumulation instead of short-circuit evaluation. The early return on length mismatch is acceptable since length is not secret information. Using codePointAt properly handles Unicode characters.

src/lib/api-key-auth.ts (1)

38-68: LGTM! Middleware implementation is secure and well-structured.

The middleware correctly:

  • Maintains backward compatibility when no keys are configured
  • Uses constantTimeEqual for timing-attack-resistant validation
  • Provides clear error messages
  • Validates against all configured keys efficiently with .some()

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