Repository navigation
fix: six mutating routes accept unauthenticated callers #86
Description
Symptom
Six POST routes declare no auth dependency, and there is no router-level or middleware guard in front of them:
POST /deals(routers/deals.py,generate_deal)POST /agentic-audience/match(routers/deals.py,agentic_audience_match)POST /api/v1/deals/curated(routers/deals.py,create_curated_deal)POST /api/v1/deals/{deal_id}/deprecate(routers/deals.py,deprecate_deal)POST /sessions/{session_id}/messages(routers/sessions.py,send_session_message)POST /sessions/{session_id}/close(routers/sessions.py,close_session_endpoint)
An anonymous caller can mint a curated deal, deprecate any deal, or close any buyer's session.
Reproduction
Three anonymous requests, no Authorization or X-Api-Key header, against the app with an empty in-memory store:
POST /api/v1/deals/DEMO-NOPE/deprecate {"reason": "probe"} -> 404 deal_not_found
POST /sessions/nope/close -> 200
POST /api/v1/deals/curated {"curator_id": "agent-range", "deal_type": "PMP"} -> 201, deal minted
A 401 would mean an auth check ran. The 404 shows the deprecate handler executed and looked the deal up; the 200 shows a session can be "closed" by anyone, even one that does not exist; the 201 is an anonymous caller minting a curated deal with a curator fee attached. The other three routes were confirmed by reading their signatures: none declares a dependency, and routers/deals.py and routers/sessions.py both construct APIRouter() with no dependencies.
Environment
seller-agent main at 8dfd5e0 (after #78), installed with uv sync --locked: Python 3.12.12, fastapi 0.139.0, iab-agentic-primitives==0.5.1. macOS 26.6. Reproduced in-process against the FastAPI app through httpx.ASGITransport, storage replaced by an in-memory double, so the numbers above are what the routes return, not what an ad server does.
Context
#39 required operator credentials on the admin surface and recorded which buyer-facing reads deliberately stay public. None of the six routes above is on that list, so their openness reads as an omission rather than a decision. #77 later closed the same gap on POST /api/v1/deals.
Expected
Deprecate and curated-deal creation are operator actions and should carry deps._require_operator_api_key_record. Session messages and close should require the buyer key that created the session, or an operator key. POST /deals and /agentic-audience/match need a stated decision either way.
Found while checking auth parity for a new list route; pre-existing on main at 8dfd5e0.
Activity