# Add async support for AbstractBaseUser.check\_password()

**URL:** <https://forum.djangoproject.com/t/add-async-support-for-abstractbaseuser-check-password/20364>\
**Category:** Async\
**Created:** [April 19, 2023, 5:25am UTC](https://forum.djangoproject.com/t/add-async-support-for-abstractbaseuser-check-password/20364 "2023-04-19T05:25:36Z")\
**Posts on this page:** 15\
**Page:** 1

<div class="post-metadata">

**Author:** ![HappyDingning](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/happydingning/32/13219_2.png) [@HappyDingning](https://forum.djangoproject.com/u/HappyDingning)\
**Post date:** [April 19, 2023, 5:25am UTC](https://forum.djangoproject.com/t/add-async-support-for-abstractbaseuser-check-password/20364/1 "2023-04-19T05:25:36Z")

</div>

When settings.PASSWORD\_HASHERS is changed and user.check\_password() is called in an async context, a SynchronousOnlyOperation exception may occur.

The reason is that the check\_password function will call the synchronous setter function to update the password field of the user table when the settings.PASSWORD\_HASHERS change.

I think this exception can be solved by defining async acheck\_password and defining async asetter function in acheck\_password. So that in an async context, user.acheck\_password() can be called to avoid the above exception.

If this solution is feasible, I am willing to contribute code.

 ![exception](https://us1.discourse-cdn.com/flex026/uploads/djangoproject/original/2X/3/3e84d176eeac8c0d1a7162ae206e30c104061c9b.png)

---

<div class="post-metadata">

**Author:** ![carltongibson](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/carltongibson/32/267_2.png) [@carltongibson](https://forum.djangoproject.com/u/carltongibson)\
**Post date:** [April 19, 2023, 5:56pm UTC](https://forum.djangoproject.com/t/add-async-support-for-abstractbaseuser-check-password/20364/2 "2023-04-19T17:56:53Z")

</div>

Hi @HappyDingning —

This is related to what @bigfootjon raised in [Asyncifying django.contrib.auth? (and signals, and maybe sessions) - #5 by bigfootjon](https://forum.djangoproject.com/t/asyncifying-django-contrib-auth-and-signals-and-maybe-sessions/18770/5) — and has been working on. It’s worth looking in over there.

> …when the settings.PASSWORD\_HASHERS change.

I’m a bit confused about this 🤔

But… we need to think about what we need to support being async. One answer is **everything** , but I wonder if we really need changing passwords to be done with async? 🤔 I’d be inclined to just use the sync views and methods for that, so explaining the use-case, the _why_ would help.

---

<div class="post-metadata">

**Author:** ![HappyDingning](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/happydingning/32/13219_2.png) [@HappyDingning](https://forum.djangoproject.com/u/HappyDingning)\
**Post date:** [April 20, 2023, 5:20am UTC](https://forum.djangoproject.com/t/add-async-support-for-abstractbaseuser-check-password/20364/3 "2023-04-20T05:20:32Z")

</div>

Thank you for your reply.

Thanks for the reminder, maybe we should discuss this at [Asyncifying django.contrib.auth? (and signals, and maybe sessions) - #5 by bigfootjon](https://forum.djangoproject.com/t/asyncifying-django-contrib-auth-and-signals-and-maybe-sessions/18770/5).

I would like to explain my views on asynchrony. I think the main benefit of async is that io will no longer block the thread, so it can improve Django’s performance.

Therefore, if we say what interfaces we need to asynchronize, I think the answer is that most of the interfaces that involve io, unless the cost of asynchronizing some interfaces is very high, they should be asynchronized.

Another thing is about why `AbstractBaseUser.check_password()` needs to be asynchronized. I think what you said makes sense, if we simply change the password, then we can do it in a synchronized view. But in the default `AUTHENTICATION_BACKENDS`, namely `ModelBackend`, `check_password()` will be called in the `authenticate` function. If `PASSWORD_HASHERS` happens to be modified in an asynchronous context, an exception may be raised.

In fact, in [Django ticket #39102](https://code.djangoproject.com/ticket/31920), in order to avoid this exception, the `sync_to_async` method is used to wrap `auth.get_user`. But I think using `sync_to_async` is only a temporary solution, because `auth.get_user` is a very frequently called function, and the asynchronous implementation using threads is not as high as native asynchronous performance after all, so we should eventually implement a native asynchronous version for `auth.get_user`.

If that’s what I said, it would make sense to add asynchronous support to `AbstractBaseUser.check_password()`, since this is the first step towards a natively asynchronous `auth.get_user`.

---

<div class="post-metadata">

**Author:** ![carltongibson](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/carltongibson/32/267_2.png) [@carltongibson](https://forum.djangoproject.com/u/carltongibson)\
**Post date:** [April 20, 2023, 5:48am UTC](https://forum.djangoproject.com/t/add-async-support-for-abstractbaseuser-check-password/20364/4 "2023-04-20T05:48:29Z")

</div>

> [@HappyDingning](#):
>
> If `PASSWORD_HASHERS` happens to be modified in an asynchronous context, an exception may be raised.

Django doesn’t support updating settings at runtime. 🤔

---

<div class="post-metadata">

**Author:** ![HappyDingning](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/happydingning/32/13219_2.png) [@HappyDingning](https://forum.djangoproject.com/u/HappyDingning)\
**Post date:** [April 20, 2023, 5:53am UTC](https://forum.djangoproject.com/t/add-async-support-for-abstractbaseuser-check-password/20364/5 "2023-04-20T05:53:46Z")

</div>

@carltongibson Sorry, I’ll rephrase.

If a user’s password hasher is different from the currently configured preferred password hasher, it may trigger the `setter` function of `check_password` to update password. The `setter` of `check_password` does not work properly in an asynchronous context. To avoid this problem, `auth.get_user()` needs to be decorated with `sync_to_async`.

But I think that using `sync_to_async` to decorate `auth.get_user()` is not a long-term solution. For better performance, we should implement the asynchronous version of `auth.get_user()`

---

<div class="post-metadata">

**Author:** ![carltongibson](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/carltongibson/32/267_2.png) [@carltongibson](https://forum.djangoproject.com/u/carltongibson)\
**Post date:** [April 20, 2023, 6:21am UTC](https://forum.djangoproject.com/t/add-async-support-for-abstractbaseuser-check-password/20364/6 "2023-04-20T06:21:03Z")

</div>

OK, gotcha 👍

I think coordinating with @bigfootjon’s work is the way forward here. There’s an ordering of work that makes sense, and he’s already hit some of the barriers.

I leave it open whether the whole stack eventually needs to be async or not: performance gains for low throughput pathways (like password checks) are more theoretical than real, so it **could** be that we never need to make this async… (Note only “could” — the point is more to focus work on things are going to need first first, regardless of a potential end-state.)

However, all effort is appreciated here! It’s nice to have another active contributor in the async space. There’s lots to do. ⛵

---

<div class="post-metadata">

**Author:** ![HappyDingning](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/happydingning/32/13219_2.png) [@HappyDingning](https://forum.djangoproject.com/u/HappyDingning)\
**Post date:** [April 20, 2023, 1:25pm UTC](https://forum.djangoproject.com/t/add-async-support-for-abstractbaseuser-check-password/20364/7 "2023-04-20T13:25:47Z")

</div>

OK, thank you for your suggestion, I will communicate with [@bigfootjon](https://forum.djangoproject.com/u/bigfootjon)

---

<div class="post-metadata">

**Author:** ![carltongibson](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/carltongibson/32/267_2.png) [@carltongibson](https://forum.djangoproject.com/u/carltongibson)\
**Post date:** [April 20, 2023, 1:39pm UTC](https://forum.djangoproject.com/t/add-async-support-for-abstractbaseuser-check-password/20364/8 "2023-04-20T13:39:26Z")

</div>

Great. I know he hit difficulties, but I’m not sure what the state of play is without going back to look at the PR/tickets.

---

<div class="post-metadata">

**Author:** ![bigfootjon](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/bigfootjon/32/12068_2.png) [@bigfootjon](https://forum.djangoproject.com/u/bigfootjon)\
**Post date:** [April 21, 2023, 2:41am UTC](https://forum.djangoproject.com/t/add-async-support-for-abstractbaseuser-check-password/20364/9 "2023-04-21T02:41:07Z")

</div>

Hey y’all. I’ll follow up on the thread I started, but just to make some points inline:

> But… we need to think about what we need to support being async. One answer is **everything** , but I wonder if we really need changing passwords to be done with async?

I can imagine a scenario where changing passwords is one of many I/O tasks that need to be completed, and using async can speed things up. For example, in a hypothetical case where the hasher was changed and all users need to upgrade when logged in. I might want to bump some count on a logging system that another user has upgraded their hash & also save the changes to the database. This is a perfect case for async IMO.

> Therefore, if we say what interfaces we need to asynchronize, I think the answer is that most of the interfaces that involve io, unless the cost of asynchronizing some interfaces is very high, they should be asynchronized…

I agree with everything in this message, I couldn’t have said it better. 😃

> I leave it open whether the whole stack eventually needs to be async or not: performance gains for low throughput pathways (like password checks) are more theoretical than real, so it **could** be that we never need to make this async… (Note only “could” — the point is more to focus work on things are going to need first first, regardless of a potential end-state.)

I’ll make a note to myself to convince you when the time comes 😛

> Great. I know he hit difficulties, but I’m not sure what the state of play is without going back to look at the PR/tickets.

I’m personally happy with the current solution, I think it’s ready when you / @felixxm have a spare minute for another round of reviews.

---

<div class="post-metadata">

**Author:** ![carltongibson](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/carltongibson/32/267_2.png) [@carltongibson](https://forum.djangoproject.com/u/carltongibson)\
**Post date:** [April 21, 2023, 5:12am UTC](https://forum.djangoproject.com/t/add-async-support-for-abstractbaseuser-check-password/20364/10 "2023-04-21T05:12:27Z")

</div>

Great @bigfootjon — thanks for the follow up.

> [@bigfootjon](#):
>
> I’m personally happy with the current solution, I think it’s ready…

If you check then flags on Trac are set correctly (uncheck the Patch needs improvement for example) it’ll show up on the Patches needing review list, and they’ll get to it. (I’m on retirement detox at the moment so am deliberately not looking for the moment. 😅)

---

<div class="post-metadata">

**Author:** ![UriahKingsley](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/uriahkingsley/32/13639_2.png) [@UriahKingsley](https://forum.djangoproject.com/u/UriahKingsley)\
**Post date:** [May 9, 2023, 1:45pm UTC](https://forum.djangoproject.com/t/add-async-support-for-abstractbaseuser-check-password/20364/11 "2023-05-09T13:45:05Z")

</div>

> [@HappyDingning](#):
>
> When settings.PASSWORD\_HASHERS is changed and user.check\_password() is called in an async context, a SynchronousOnlyOperation exception may occur.
> 
> The reason is that the check\_password function will call the synchronous setter function to update the password field of the user table when the settings.PASSWORD\_HASHERS change.
> 
> I think this exception can be solved by defining async acheck\_password and defining async asetter function in acheck\_password. So that in an async context, user.acheck\_password() can be called to avoid the above exception.
> 
> If this solution is feasible, I am willing to contribute code.

It is indeed possible to solve the Synchronous only operation exception by defining an asynchronous check password function and an asynchronous setter function in acheck password. By doing so, you can call user.acheck\_password() in an async contect, avoiding the exception. If you believe this solution is feasible and you are willing to contribute code, you can proceed by implementing the async versions of the check password and setter functions. Make sure to thoroughly test the changes and consider the impact on the overall system before submitting the code contribution.

---

<div class="post-metadata">

**Author:** ![HappyDingning](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/happydingning/32/13219_2.png) [@HappyDingning](https://forum.djangoproject.com/u/HappyDingning)\
**Post date:** [May 10, 2023, 7:14am UTC](https://forum.djangoproject.com/t/add-async-support-for-abstractbaseuser-check-password/20364/12 "2023-05-10T07:14:02Z")

</div>

Thank you for your reply. In order to improve the PR pass rate, I am afraid that I will not take actual actions until this proposal has more support from the community.

If you need this functionality, please have a look under [this repository](https://github.com/HappyDingning/django/tree/async_check_password). This is the initial implementation of `acheck_password` and its test cases

---

<div class="post-metadata">

**Author:** ![bigfootjon](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/bigfootjon/32/12068_2.png) [@bigfootjon](https://forum.djangoproject.com/u/bigfootjon)\
**Post date:** [May 12, 2023, 2:15am UTC](https://forum.djangoproject.com/t/add-async-support-for-abstractbaseuser-check-password/20364/13 "2023-05-12T02:15:51Z")

</div>

> [@HappyDingning](#):
>
> Thank you for your reply. In order to improve the PR pass rate, I am afraid that I will not take actual actions until this proposal has more support from the community.

Hey @HappyDingning, can you clarify what you mean here? As far as I can tell from reading this thread this idea has plenty of support, it certainly has mine! The commits on your repository look to be in really good shape, I think you could create a Pull Request with those commits.

The only missing detail is that you need to open a ticket and get it accepted before creating the Pull Request. Go here to create one: [https://code.djangoproject.com/](https://code.djangoproject.com/) (you can log in with your GitHub account).

Add myself (Jon Janzen) and @carltongibson (Carlton Gibson) to the “CC” section of the ticket. Also a good idea to add “async” to the “keywords” section of the ticket.

Assuming someone accepts the ticket you can create a Pull Request. It’ll possibly be a few rounds of review on the PR before it is accepted, but the commits you have now look like a great start!

---

<div class="post-metadata">

**Author:** ![HappyDingning](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/happydingning/32/13219_2.png) [@HappyDingning](https://forum.djangoproject.com/u/HappyDingning)\
**Post date:** [May 12, 2023, 7:31am UTC](https://forum.djangoproject.com/t/add-async-support-for-abstractbaseuser-check-password/20364/14 "2023-05-12T07:31:43Z")

</div>

Thank you for your reply. My main concern is that the ticket will not be accepted. But after hearing what you said, I think I can give it a try. I’ll tidy up the code soon and try to create a ticket. 😁

---

<div class="post-metadata">

**Author:** ![carltongibson](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/carltongibson/32/267_2.png) [@carltongibson](https://forum.djangoproject.com/u/carltongibson)\
**Post date:** [May 12, 2023, 10:06am UTC](https://forum.djangoproject.com/t/add-async-support-for-abstractbaseuser-check-password/20364/15 "2023-05-12T10:06:39Z")

</div>

@HappyDingning — also link back to this discussion when creating the ticket. 👍
