Hasher / authentication error in Django 5

Dear Community,

we recently tried updating to Django 5.0.3, and noticed that we cannot authenticate our existing users anymore.

We use the authenticate() function from django.contrib.auth, and this works perfectly in the 4.2 line. However, after upgrading to the 5.0 line, we started to get strange errors about password field cannot be updated in the DB. I do not understand why a password reading and validating operation would write anything to the DB.

Is there something i am missing or this is some bug?

Please see below the exact stack trace:

Internal Server Error: /api/login/
Traceback (most recent call last):
  File "/opt/venv/lib/python3.12/site-packages/django/core/handlers/exception.py", line 55, in inner
    response = get_response(request)
               ^^^^^^^^^^^^^^^^^^^^^
  File "/opt/venv/lib/python3.12/site-packages/django/core/handlers/base.py", line 197, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<directory_of_our_application>/views.py", line 63, in login
    user_instance = authenticate(request, username = username, password = password)
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/venv/lib/python3.12/site-packages/django/views/decorators/debug.py", line 75, in sensitive_variables_wrapper
    return func(*func_args, **func_kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/venv/lib/python3.12/site-packages/django/contrib/auth/__init__.py", line 79, in authenticate
    user = backend.authenticate(request, **credentials)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/venv/lib/python3.12/site-packages/django/contrib/auth/backends.py", line 48, in authenticate
    if user.check_password(password) and self.user_can_authenticate(user):
       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/venv/lib/python3.12/site-packages/django/contrib/auth/base_user.py", line 125, in check_password
    return check_password(raw_password, self.password, setter)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/venv/lib/python3.12/site-packages/django/contrib/auth/hashers.py", line 77, in check_password
    setter(password)
  File "/opt/venv/lib/python3.12/site-packages/django/contrib/auth/base_user.py", line 123, in setter
    self.save(update_fields=["password"])
  File "/opt/venv/lib/python3.12/site-packages/django/contrib/auth/base_user.py", line 78, in save
    super().save(*args, **kwargs)
  File "/opt/venv/lib/python3.12/site-packages/django/db/models/base.py", line 822, in save
    self.save_base(
  File "/opt/venv/lib/python3.12/site-packages/django/db/models/base.py", line 909, in save_base
    updated = self._save_table(
              ^^^^^^^^^^^^^^^^^
  File "/opt/venv/lib/python3.12/site-packages/django/db/models/base.py", line 1042, in _save_table
    raise DatabaseError("Save with update_fields did not affect any rows.")
django.db.utils.DatabaseError: Save with update_fields did not affect any rows.

The password is probably updated behind the scene because of the change of iterations number in the PKBDF2 hasher (see Django 5.0 release notes | Django documentation | Django).

The fact that an update is attempted means that authentication succeeded, but as the number of iterations for actual password does not match the new requirements, the password is updated with this new number of iterations.

So, yes, the write to db is normal. But, I don’t know why the update fails in such manner. Do you have a specific user model ? If so, can you share its definition ?

Thank you for the clarification. I would not have expected that iteration number change would result in a change of the hash, therefore i would not have expected a DB write - but still i can accept and be thankful for the explanation :slight_smile:

I do have a custom user model which unfortunately i cannot share, but regarding the password it just routes back to the AbstractBaseUser model.

The details - and possible explanation for the error are documented at Password upgrading.
Are you using a custom or customized password hashing technique?

1 Like

Thank you, Ken, for your reply!

Indeed, “When users log in, if their passwords are stored with anything other than the preferred algorithm, Django will automatically upgrade the algorithm to the preferred one.” explains why there is DB writing.

And it seems i found what caused the django.db.utils.DatabaseError: Save with update_fields did not affect any rows., i.e. what i missed earlier: Migrating existing UUIDField on MariaDB 10.7+

Thank you again for the guidance,