Hey,
Sorry about the delay in my response, holidays came early and stayed late for
me this year.
> TBH I'd prefer it if you pondered the design here without opening a ticket
> until such a point (if ever) that you have a concrete plan. (We'd likely just
> close it as wontfix unless there's a specific idea on the table anyway, so
> it's just noise at that point.)
Understood, sorry for my ignorance on the process. I appreciate your patience!
>>> There's: https://code.djangoproject.com/ticket/31949 "Allow builtin view
>>> decorators to be applied directly to async views."
>>> I think this is likely the next step.
>>>
>>> There's a PR for that, which I think took a too complex approach (see
>>> discussion). A simpler (more inline) take be good to see.
>>
>> Thanks, I saw this ticket but it didn't look relevant when I was skimming
>> the tracker. I'll take a closer look.
Replying to myself here, I took a look at this ticket and associated PRs and
that’s not quite do what I’m looking for (even if all the various constituent
parts got merged) but the changes to the `auth` decorators are related.
I’m interested in an async interface of the auth app itself, i.e. the
functionality exposed in `django/contrib/auth/__init__.py`.
(the next few paragraphs are background info on my personal investment in this,
feel free to skip to the section marked “Proposal")
For some background on my interest in this: I’m running Django as an asgi app
and all codepaths down to the django framework boundary are async. That means
all my middleware, views, and ORM usage are all using the async versions, where
applicable/possible. There are a number of reasons for this, but the most
notable are simplicity (easier to reason about code if it is all either sync or
async) and efficiency (most of the code is GraphQL resolvers which are more
efficient to execute concurrently).
Also important to know that I almost exclusively use django as an API server,
there is only one template for all “views” which just loads a javascript
webpack bundle and renders using React, which then fetches data from the server
using GraphQL. Effectively, that means I‘m calling the django auth APIs
directly instead of using the default `LoginView` or `login_required` or
anything like that.
Anyway, right now almost all of the sync/async boundaries are “invisible” to me
in that they are inside Django. I’ve replaced all my own wrappers around the
ORM’s synchronous-only methods with the `a`-prefixed methods provided over the
last few Django releases (and that will be provided in 4.2!). So `aget` instead
of `get`, `acreate` instead of `create` and so forth.
One area of async/sync boundaries that is currently prevalent in my codebase is
my `sync_to_async` wrappers around `django.contrib.auth` methods. I’d like to
push those down into Django, and then as deep into Django as is expedient right
now.
# Proposal
Add asynchronous versions of the auth app’s API (i.e. `__init__.py`), then
allow backends to have async-native versions and use that from the public API,
and finally update the provided middleware (and maybe decorators) to use
async-native functionality if they are running in ASGI mode.
## Part 1: Async API
The “simple” part of this proposal is just to define a bunch of functions with
“a” prefixes in `__init__.py` and use `sync_to_async` to call the synchronous
versions as has been done to, for example, QuerySet. This would involve
asynchronous versions of the following functions in `auth/__init__.py`:
* `get_user`
* `authenticate`
* `login`
* `update_session_auth_hash`
* `logout`
## Part 2: async backends
I think once that interface is defined it makes sense to try and make as much
of this framework as natively async-compatible as possible. That would mean
adding support for async auth backends and then connecting the async API
methods (as enumerated above) to the async versions of the backend methods.
This could mean that there would be no `sync_to_async` calls within the `auth`
app itself (except for sessions and signals, see Open Questions below), and any
sync/async boundaries would be “below” the `auth` app. For example, for
`ModelBackend` and its children the sync/async boundary would be in the ORM
layer, as `QuerySet` presents an async API that is currently just a wrapper
around the sync internals.
Allowing async backends would involve introducing the following async functions
to `BaseBackend` that just call the sync versions by default, and can be
overridden in child classes to have natively async versions:
* `authenticate`
* `get_user`
* `get_user_permissions`
* `get_group_permissions`
* `get_all_permissions`
* `has_perm`
Then `ModelBackend` would be augmented to have async-native versions of the
above. `RemoteUserBackend` would also need some tweaks to be async-native, but
overall this isn’t terribly much work. Oh and to support `ModelBackend` being
async-native `BaseUserModel` would need an