security: require auth token and restrict CORS on viewer API - #26
Conversation
The viewer exposed trace, evaluation, memory, annotation, explorer, OTLP and journal APIs with no authorization while listening on all interfaces, and its CORS policy allowed every origin, method and header. Add a `_require_viewer_authorization` dependency on every API router and every `/v1` ingest/sync endpoint. When `NOOA_VIEWER_AUTH_TOKEN` is set, requests must send `Authorization: Bearer <token>`, compared with `hmac.compare_digest()`. When it is unset, loopback clients keep working so existing local development and ingest clients are unaffected, while non-loopback clients get a 403. Restrict CORS to `http://localhost:5001` and `http://127.0.0.1:5001` by default, overridable via a comma-separated `NOOA_VIEWER_CORS_ORIGINS`, and narrow allowed methods and headers to the ones the viewer APIs use. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2d7cc0d to
ba42b28
Compare
Security issueThe viewer exposed trace, evaluation, memory, annotation, playground, OTLP, and journal APIs without an authorization check while listening on all interfaces. Its CORS policy allowed every origin, method, and header. A network client or malicious browser origin could read stored agent information or invoke state-changing routes. |
PatchSet The token is read at request time and compared with CORS now allows only |
The viewer exposed trace, evaluation, memory, annotation, explorer, OTLP and journal APIs with no authorization while listening on all interfaces, and its CORS policy allowed every origin, method and header.
Add a
_require_viewer_authorizationdependency on every API router and every/v1ingest/sync endpoint. WhenNOOA_VIEWER_AUTH_TOKENis set, requests must sendAuthorization: Bearer <token>, compared withhmac.compare_digest(). When it is unset, loopback clients keep working so existing local development and ingest clients are unaffected, while non-loopback clients get a 403.Restrict CORS to
http://localhost:5001andhttp://127.0.0.1:5001by default, overridable via a comma-separatedNOOA_VIEWER_CORS_ORIGINS, and narrow allowed methods and headers to the ones the viewer APIs use.What does this PR do?
Related issues
Checklist
uv run ruff check .anduv run ruff format --check .pass)uv run pytest)