Summary
api/server.py enforces the bearer token by reading the Authorization header manually inside the check_auth dependency, but never registers a FastAPI/OpenAPI security scheme. The generated OpenAPI document therefore contains no securitySchemes and no security requirements, so:
- Swagger UI (
/docs) shows no "Authorize" button — there is no way to supply the token in the browser.
- Every protected endpoint invoked from
/docs (or via the Swagger-generated curl) is sent without an Authorization header and returns 401 {"detail":"Invalid or missing API token"}.
This makes the bundled Swagger UI unusable against any deployment that sets VIRP_API_TOKEN.
Environment
- Repo at commit
604edca (api/server.py last modified b937b1a, 2026-04-24).
- FastAPI app
VIRP Appliance 0.1.0-poc, OpenAPI 3.1.0, uvicorn.
VIRP_API_TOKEN set (auth enforced).
Reproduce
- Run the API with
VIRP_API_TOKEN=<token> and open /docs.
GET /api/devices -> Execute -> 401 Invalid or missing API token.
GET /openapi.json -> components has no securitySchemes; /api/devices GET has no security.
Expected
/docs shows an Authorize control; after entering the token once, Swagger attaches Authorization: Bearer <token> to every protected request and they succeed.
Root cause
check_auth(request: Request) reads request.headers.get("Authorization") directly — correct at runtime, but invisible to OpenAPI, so the schema advertises no auth and Swagger cannot prompt for it.
Suggested fix (minimal, backward-compatible)
Declare an HTTPBearer scheme and consume it via Security(...) so it lands in the OpenAPI doc, keeping the constant-time compare and the existing "no token set = dev mode" behaviour:
from fastapi import Security
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
bearer_scheme = HTTPBearer(auto_error=False)
async def check_auth(creds: HTTPAuthorizationCredentials | None = Security(bearer_scheme)):
if not API_TOKEN:
return # dev/POC mode — no auth configured
if creds is None or not hmac.compare_digest(creds.credentials, API_TOKEN):
raise HTTPException(status_code=401, detail="Invalid or missing API token")
auto_error=False preserves the current 401 message and the unauthenticated dev-mode path. Existing clients that send Authorization: Bearer ... are unaffected; the only change is that OpenAPI now advertises the scheme and Swagger renders Authorize. (The audit_identity() header read can stay as-is.)
Notes
Found while standing up the bundled API in an isolated Cisco lab (read-only observation use). Happy to open a PR with the above if useful. Minor aside (not part of this issue): endpoints are served under /api/* while the integration spec describes /v1/*.
Summary
api/server.pyenforces the bearer token by reading theAuthorizationheader manually inside thecheck_authdependency, but never registers a FastAPI/OpenAPI security scheme. The generated OpenAPI document therefore contains nosecuritySchemesand nosecurityrequirements, so:/docs) shows no "Authorize" button — there is no way to supply the token in the browser./docs(or via the Swagger-generatedcurl) is sent without anAuthorizationheader and returns401 {"detail":"Invalid or missing API token"}.This makes the bundled Swagger UI unusable against any deployment that sets
VIRP_API_TOKEN.Environment
604edca(api/server.pylast modifiedb937b1a, 2026-04-24).VIRP Appliance 0.1.0-poc, OpenAPI3.1.0, uvicorn.VIRP_API_TOKENset (auth enforced).Reproduce
VIRP_API_TOKEN=<token>and open/docs.GET /api/devices-> Execute ->401 Invalid or missing API token.GET /openapi.json->componentshas nosecuritySchemes;/api/devicesGET has nosecurity.Expected
/docsshows an Authorize control; after entering the token once, Swagger attachesAuthorization: Bearer <token>to every protected request and they succeed.Root cause
check_auth(request: Request)readsrequest.headers.get("Authorization")directly — correct at runtime, but invisible to OpenAPI, so the schema advertises no auth and Swagger cannot prompt for it.Suggested fix (minimal, backward-compatible)
Declare an
HTTPBearerscheme and consume it viaSecurity(...)so it lands in the OpenAPI doc, keeping the constant-time compare and the existing "no token set = dev mode" behaviour:auto_error=Falsepreserves the current 401 message and the unauthenticated dev-mode path. Existing clients that sendAuthorization: Bearer ...are unaffected; the only change is that OpenAPI now advertises the scheme and Swagger renders Authorize. (Theaudit_identity()header read can stay as-is.)Notes
Found while standing up the bundled API in an isolated Cisco lab (read-only observation use). Happy to open a PR with the above if useful. Minor aside (not part of this issue): endpoints are served under
/api/*while the integration spec describes/v1/*.