Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions services/admin-api/app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ class PaginatedMeetingUserStatResponse(BaseModel):
items: List[MeetingUserStat]

# Security - Reuse logic from meeting-api/auth.py for admin token verification
API_KEY_HEADER = APIKeyHeader(name="X-Admin-API-Key", auto_error=False) # Use a distinct header
USER_API_KEY_HEADER = APIKeyHeader(name="X-API-Key", auto_error=False) # For user-facing endpoints
API_KEY_HEADER = APIKeyHeader(name="X-Admin-API-Key", scheme_name="AdminApiKey", auto_error=False)
USER_API_KEY_HEADER = APIKeyHeader(name="X-API-Key", scheme_name="UserApiKey", auto_error=False)
ADMIN_API_TOKEN = os.getenv("ADMIN_API_TOKEN") # Read from environment
ANALYTICS_API_TOKEN = os.getenv("ANALYTICS_API_TOKEN") # Read-only analytics token

Expand Down
33 changes: 33 additions & 0 deletions services/admin-api/tests/test_openapi_schema.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
"""Regression test for issue #80 — OpenAPI security scheme names must be distinct.

FastAPI collapses APIKeyHeader instances that share the same scheme_name into one
OpenAPI security scheme. Without explicit scheme_name values, Swagger UI shows the
wrong header (X-API-Key) in curl examples for admin endpoints.
"""

import os

os.environ.setdefault("ADMIN_API_TOKEN", "test-admin-token")
os.environ.setdefault("DB_HOST", "localhost")
os.environ.setdefault("DB_PORT", "5432")
os.environ.setdefault("DB_NAME", "test")
os.environ.setdefault("DB_USER", "test")
os.environ.setdefault("DB_PASSWORD", "test")

import pytest
from httpx import AsyncClient, ASGITransport
from app.main import app


@pytest.mark.asyncio
async def test_openapi_security_schemes_are_distinct():
"""AdminApiKey and UserApiKey must be separate schemes so Swagger shows the right header."""
transport = ASGITransport(app=app)
async with AsyncClient(transport=transport, base_url="http://test") as client:
resp = await client.get("/openapi.json")
assert resp.status_code == 200
schemes = resp.json()["components"]["securitySchemes"]
assert "AdminApiKey" in schemes, "AdminApiKey scheme missing — admin curl examples will show wrong header"
assert "UserApiKey" in schemes, "UserApiKey scheme missing — user curl examples will show wrong header"
assert schemes["AdminApiKey"]["name"] == "X-Admin-API-Key"
assert schemes["UserApiKey"]["name"] == "X-API-Key"