Opened 4 hours ago
Closed 4 hours ago
#37042 closed Bug (duplicate)
alogin() does not invalidate _acached_user, causing stale AnonymousUser after async login
| Reported by: | Dong Huynh | Owned by: | |
|---|---|---|---|
| Component: | contrib.auth | Version: | 5.2 |
| Severity: | Normal | Keywords: | alogin, async, auser |
| Cc: | Triage Stage: | Unreviewed | |
| Has patch: | no | Needs documentation: | no |
| Needs tests: | no | Patch needs improvement: | no |
| Easy pickings: | yes | UI/UX: | no |
Description
When alogin() is called after request.auser() in the same request, subsequent calls to request.auser() return a stale AnonymousUser because alogin() does not update request._acached_user.
The sync path does not have this problem. login() sets request.user = user, which replaces the SimpleLazyObject entirely, so all subsequent request.user access returns the correct user. But in the async path, request.auser() is a separate callable (partial(auser, request)) that caches its result in request._acached_user. alogin() updates request.user but never touches _acached_user.
Reproduction:
class MyAuthMiddleware:
async def __acall__(self, request):
# Step 1: check if already authenticated
user = await request.auser() # caches AnonymousUser in _acached_user
if not user.is_authenticated:
db_user = await User.objects.aget(email="existing@example.com")
await alogin(request, db_user) # updates request.user, NOT _acached_user
response = await self.get_response(request) # view runs
return response
# In the view:
async def my_view(request):
user = await request.auser() # returns stale AnonymousUser
assert user.is_authenticated # FAILS
Fixed in: Django 6.x, which replaces request.auser with a closure after login:
if hasattr(request, "auser"):
async def auser():
return user
request.auser = auser
Workaround for 5.x, after await alogin(request, user):
request._acached_user = user # Fixed in Django 6.x (alogin replaces request.auser)
Duplicate of #36540 which (as you said) is now fixed from 6.0