security(auth): strict JWT validation in middleware (fix junk cookie bypass)

AUTH_TEST_PLAN test 7.5.8 expects junk cookies to be rejected with
401. The previous middleware behaviour was "presence-only": check
that some access_token cookie exists, then pass through. In
combination with my Task-12 decision to skip @require_auth
decorators on routes, this created a gap where a request with any
cookie-shaped string (e.g. access_token=not-a-jwt) would bypass
authentication on routes that do not touch the repository
(/api/models, /api/mcp/config, /api/memory, /api/skills, …).

Fix: middleware now calls get_current_user_from_request() strictly
and catches the resulting HTTPException to render a 401 with the
proper fine-grained error code (token_invalid, token_expired,
user_not_found, …). On success it stamps request.state.user and
the contextvar so repository-layer owner filters work downstream.

The 4 old "_with_cookie_passes" tests in test_auth_middleware.py
were written for the presence-only behaviour; they asserted that
a junk cookie would make the handler return 200. They are renamed
to "_with_junk_cookie_rejected" and their assertions flipped to
401. The negative path (no cookie → 401 not_authenticated)
is unchanged.

Verified:
  no cookie       → 401 not_authenticated
  junk cookie     → 401 token_invalid     (the fixed bug)
  expired cookie  → 401 token_expired

Tests: 284 passed (auth + persistence + isolation)
Lint: clean
This commit is contained in:
greatmengqi
2026-04-08 13:18:44 +08:00
parent e7a881b577
commit 745bf4324e
2 changed files with 52 additions and 24 deletions
+14 -8
View File
@@ -161,9 +161,12 @@ def test_protected_path_no_cookie_returns_401(client):
assert body["detail"]["code"] == "not_authenticated"
def test_protected_path_with_cookie_passes(client):
def test_protected_path_with_junk_cookie_rejected(client):
"""Junk cookie → 401. Middleware strictly validates the JWT now
(AUTH_TEST_PLAN test 7.5.8); it no longer silently passes bad
tokens through to the route handler."""
res = client.get("/api/models", cookies={"access_token": "some-token"})
assert res.status_code == 200
assert res.status_code == 401
def test_protected_post_no_cookie_returns_401(client):
@@ -189,16 +192,18 @@ def test_protected_patch_no_cookie(client):
assert res.status_code == 401
def test_put_with_cookie_passes(client):
def test_put_with_junk_cookie_rejected(client):
"""Junk cookie on PUT → 401 (strict JWT validation in middleware)."""
client.cookies.set("access_token", "tok")
res = client.put("/api/mcp/config")
assert res.status_code == 200
assert res.status_code == 401
def test_delete_with_cookie_passes(client):
def test_delete_with_junk_cookie_rejected(client):
"""Junk cookie on DELETE → 401 (strict JWT validation in middleware)."""
client.cookies.set("access_token", "tok")
res = client.delete("/api/threads/abc")
assert res.status_code == 200
assert res.status_code == 401
# ── Fail-closed: unknown future endpoints ─────────────────────────────────
@@ -210,7 +215,8 @@ def test_unknown_endpoint_no_cookie_returns_401(client):
assert res.status_code == 401
def test_unknown_endpoint_with_cookie_passes(client):
def test_unknown_endpoint_with_junk_cookie_rejected(client):
"""New endpoints are also protected by strict JWT validation."""
client.cookies.set("access_token", "tok")
res = client.get("/api/future-endpoint")
assert res.status_code == 200
assert res.status_code == 401