Part of #59.
Part of the broken-access-control remediation epic. Depends on Phase 1. Highest severity — these routes require no authentication at all today.
Findings addressed
Work
- Switch these handlers from
get_current_user_optional to mandatory get_current_user.
- Apply the Phase‑1 helper against the resolved DB user for each
student_id/item_id/nudge_id.
- For
jobs.py: record owner (Job.user_id/student_id already exist) and check it; authenticate the WS on connect (token via query param or subprotocol) before streaming.
Definition of Done
Affected files
src/api/handlers/practice.py, src/api/handlers/nudges.py, src/api/handlers/jobs.py, src/api/middleware/auth.py
Part of #59.
Part of the broken-access-control remediation epic. Depends on Phase 1. Highest severity — these routes require no authentication at all today.
Findings addressed
practice.py(:112assign,:426complete,:539summary) — all useget_current_user_optional;current_useris never read. Unauthenticated callers can create assignments for anystudent_id, mutate any student's Elo/completion viaitem_id, and read any student's performance.nudges.py(:37check,:126users/{user_id},:298engage) — same optional-auth pattern; also triggers emails to arbitrary users.jobs.py(:27GET,:62WebSocket) — GET has unused optional auth; the WebSocket has no auth dependency at all and streams another student'sresult.Work
get_current_user_optionalto mandatoryget_current_user.student_id/item_id/nudge_id.jobs.py: record owner (Job.user_id/student_idalready exist) and check it; authenticate the WS on connect (token via query param or subprotocol) before streaming.Definition of Done
401).403(Phase‑1 tests green for these routes).Affected files
src/api/handlers/practice.py,src/api/handlers/nudges.py,src/api/handlers/jobs.py,src/api/middleware/auth.py