Skip to content

fix(auth): make require_auth async so the user ContextVar reaches the endpoint#485

Open
truffle-dev wants to merge 1 commit into
HKUDS:devfrom
truffle-dev:fix/auth-contextvar-async-481
Open

fix(auth): make require_auth async so the user ContextVar reaches the endpoint#485
truffle-dev wants to merge 1 commit into
HKUDS:devfrom
truffle-dev:fix/auth-contextvar-async-481

Conversation

@truffle-dev
Copy link
Copy Markdown
Contributor

Description

When AUTH_ENABLED=true, every non-admin HTTP request hits 404 because the user ContextVar set inside require_auth is invisible by the time the endpoint resolves the per-user path service.

require_auth is declared as a sync def. FastAPI dispatches sync deps via anyio.to_thread.run_sync, which captures the request context with copy_context() and runs the function in a worker thread under that copy. Mutations made inside the thread, including ContextVar.set, live on the copy and are discarded when the thread returns. The endpoint then runs in the original event-loop context with _current_user unset, so get_current_path_service() falls back to PathService.get_instance(), which points at data/user/ — the admin workspace. Admin requests appear to work because the fallback coincides with their intended workspace; user requests resolve to the admin DB and 404 on every session lookup.

The user-context middleware that #474 introduced for exactly this case was removed in 8bae3ca on the assumption that require_auth was sufficient. With require_auth running in a threadpool, it was not.

Declaring require_auth (and require_admin, for symmetry) as async def keeps the dependency on the event loop; the ContextVar mutation now propagates to the endpoint and per-user path resolution works.

Related Issues

Module(s) Affected

  • api
  • tests

Checklist

  • I have read and followed the contribution guidelines.
  • My code follows the project's coding standards.
  • I have run pre-commit run --all-files and fixed any issues.
  • I have added relevant tests for my changes.
  • I have updated the documentation (if necessary).
  • My changes do not introduce any new security vulnerabilities.

Additional Notes

Regression coverage in tests/api/test_auth_contextvar.py pins two invariants:

  1. require_auth and require_admin are declared async.
  2. With AUTH_ENABLED=true and a valid token, the user ContextVar set inside require_auth is visible from inside the endpoint, so get_path_service() resolves to the per-user multi-user/<uid>/ workspace.

Verified locally with pytest tests/api/test_auth_contextvar.py — all four cases pass on the fix and fail on the prior sync def.

… endpoint

Closes HKUDS#481.

When AUTH_ENABLED=true, every non-admin HTTP request hit 404 because the
user ContextVar set inside require_auth was invisible by the time the
endpoint resolved the per-user path service.

require_auth was declared as a sync def. FastAPI dispatches sync deps via
anyio.to_thread.run_sync, which captures the request context with
copy_context() and runs the function in a worker thread under that copy.
Mutations made inside the thread, including ContextVar.set, live on the
copy and are discarded when the thread returns. The endpoint then ran in
the original event-loop context with _current_user unset, so
get_current_path_service() fell back to PathService.get_instance(), which
points at data/user/ -- the admin workspace. Admin requests appeared to
work because the fallback coincided with their intended workspace; user
requests resolved to the admin DB and 404'd on every session lookup.

The user-context middleware that PR HKUDS#474 introduced for exactly this
case was removed in 8bae3ca on the assumption that require_auth was
sufficient. With require_auth running in a threadpool, it was not.

Declaring require_auth (and require_admin, for symmetry) as async def
keeps the dependency on the event loop; the ContextVar mutation now
propagates to the endpoint and per-user path resolution works.

Regression coverage in tests/api/test_auth_contextvar.py pins both
invariants: the dependencies are async, and the user ContextVar set
inside require_auth is observable from the endpoint and from
get_path_service().
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant