📌

dj-rest-authでパスワードリセットメールをカスタマイズする

2023/04/12に公開

はじめに

今、フロントエンドに NextJS、バックエンドに Django REST Framework を用いたアプリケーションを開発しています。これまでにユーザ認証には Google 認証を用いており、ローカルアカウント認証は用いていませんでした。ある事情でローカルアカウント認証を追加することになりましたが、パスワードリセット機能の作成で思いのほかハマったのでメモしておきます。

やりたかったこと

こんなことやろうとしてました。至って普通の機能です。

  1. パスワードがわからなくなった時、ユーザはパスワードリセットのためのメール送信画面に遷移できる
  2. 1 の画面からフロントエンドからメールアドレスを記入し、パスワードリセットするとメールが送信される
  3. ユーザはメールに記載された URL をクリックすると、パスワードリセット画面に遷移できる
  4. パスワードリセット画面で新旧パスワードを入力する

この 2 の工程ではバックエンドからメールが送られますが、その時の文面はデフォルトでは

You're receiving this email because you requested a password reset for your user account at example.com.
Please go to the following page and choose a new password:

http://localhost:8000/api/v1/auth/password/reset/confirm/<uuid>/<token>

となります。ここにアクセスするとユーザはバックエンド側のページに飛びます。

しかし、開発中のアプリケーションは、フロントエンド側からバックエンドの API を叩くような作りになっているので、このメールに書いてある URL を

http://<フロントエンドのオリジン>/auth/password/reset/confirm/<uuid>/<token>

みたいな感じにして、パスワードリセット画面を NextJS 側で作りたいと思いました。

これを実現するにはメール本文の URL をフロントエンドの書き換えればいいわけですが、一筋縄ではいきませんでした。

解決方法

この Q&Aに加えて、以下のように
password_reset_form_class をオーバーライドする。

from dj_rest_auth.serializers import PasswordResetSerializer
from django.conf import settings
from dj_rest_auth.forms import AllAuthPasswordResetForm
from django.contrib.auth.forms import PasswordResetForm

class CustomPasswordResetSerializer(PasswordResetSerializer):
    @property
    def password_reset_form_class(self):
        use_custom_email_template = bool(self.get_email_options().get("html_email_template_name", ''))
        if 'allauth' in settings.INSTALLED_APPS and not use_custom_email_template:
            return AllAuthPasswordResetForm
        else:
            return PasswordResetForm

    def get_email_options(self):
        return {
            'html_email_template_name': 'custom_password_reset_email.html',
        }

ハマりポイント

現在使用している django のバージョン(4.1.7)では、CustomSerializer で get_email_options をオーバーライドしようとしても、無視されるようである。これはなかなかわからんかった・・・。

テンプレートのパスがおかしい?と思い、serializer の中でテンプレートを読み込めば普通に読み込めるので、謎すぎる挙動でした。

Discussion