Django 6.0から6.1へアップデートしたときに躓いたのでその備忘録になります。
TL;DR
- Django 6.1 で
auth.login()がuser.get_session_auth_hash()を無条件で呼ぶようになりました - 解決策として
AbstractBaseUserの 3 メソッドを移植する必要があります
問題
Django 6.0 → 6.1 に上げたら、ログインを伴う処理が全てこのエラーで落ちました。
AttributeError: 'User' object has no attribute 'get_session_auth_hash'
原因
django/contrib/auth/__init__.py の login() にあった hasattr ガードが 6.1 で消えました。
# Django 6.0
session_auth_hash = ""
# RemovedInDjango61Warning.
if hasattr(user, "get_session_auth_hash"):
session_auth_hash = user.get_session_auth_hash()
# Django 6.1
session_auth_hash = user.get_session_auth_hash()
AbstractBaseUser を継承していれば元から実装済みなので無関係ですが、継承せずに is_authenticated や check_password() を手で生やしている独自 User モデルは影響を受けるので次節の解決策対応が必要となります。
解決策
AbstractBaseUser の実装を、自分のパスワード列に合わせて移植します。
下記はあくまで一例です。
from django.conf import settings
from django.utils.crypto import salted_hmac
class User(models.Model):
password_hash = models.CharField(max_length=255) # 独自のパスワード列
def get_session_auth_hash(self):
return self._get_session_auth_hash()
def get_session_auth_fallback_hash(self):
for fallback_secret in settings.SECRET_KEY_FALLBACKS:
yield self._get_session_auth_hash(secret=fallback_secret)
def _get_session_auth_hash(self, secret=None):
key_salt = "myapp.models.User.get_session_auth_hash"
return salted_hmac(
key_salt,
self.password_hash,
secret=secret,
algorithm="sha256",
).hexdigest()
上記3メソッドがauth.login()時に実行されるので実装しましょう。
-
get_session_auth_fallback_hash()も必要です。get_user()がハッシュ検証失敗時に無条件で呼ぶためです -
key_saltはAbstractBaseUserと別の文字列にしておきます (将来AbstractBaseUserに移行したときハッシュが偶然一致しないように)
注意点
既存セッション失効による強制ログアウト
get_session_auth_hash() を実装すると、既存セッションが全て失効するのでログインしているユーザが一同にログアウトします。既存セッションにはハッシュが空文字で保存されているため、検証に失敗して session.flush() されます。つまりデプロイ直後に全利用者が強制ログアウトされ、再ログインが必要になります。