We had a problem with a website which uses Django. Each time we upgrade Django, if a user is logged in with two or more different browsers, and then they login again from one browser - they are automatically logged out from all other sessions (browsers). Since we upgraded Django to new major versions about 5 times in the last year, this caused us a headache. We don't want to force users to have to login again and again between sessions. How can we solve this problem?
We checked and found out that this problem is caused due to a change in PBKDF2PasswordHasher.iterations
between versions of Django. Each time we upgrade Django to a new major version (such as from 3.0 to 3.1), PBKDF2PasswordHasher.iterations
changes. This causes the user's hashed password to be calculated again the next time the user logs in, which forces the user to remain logged out in all other sessions. I even created a ticket with Django's tracking system.
There are two options to fix this issue. First, we can patch class PBKDF2PasswordHasher
to keep the number of iterations
constant, and also update def must_update
:
from django.contrib.auth.hashers import PBKDF2PasswordHasher
def patch():
def must_update(self, encoded):
# Update the stored password only if the iterations diff is at least 250,000.
algorithm, iterations, salt, hash = encoded.split('$', 3)
iterations_diff = abs(self.iterations - int(iterations))
return ((int(iterations) != self.iterations) and (iterations_diff >= 250000))
PBKDF2PasswordHasher.iterations = 180000 # Django 3.0.x
PBKDF2PasswordHasher.must_update = must_update
And then in our base AppConfig
class:
class SpeedyCoreBaseConfig(AppConfig):
name = 'speedy.core.base'
verbose_name = _("Speedy Core Base App")
label = 'base'
def ready(self):
locale_patches.patch() # Another patch
session_patches.patch() # This patch
Or, you can inherit a new class from PBKDF2PasswordHasher
, change iterations
and def must_update
, and use your new class in the settings (PASSWORD_HASHERS
). We used the first option, although it might be better to use the second option (inherit a new class) from a software engineering perspective. They both work.