Djangoでrest frameworkをつかってAPIを実装していて、ユーザのパスワードリセット機能を実装しようとした時の備忘録です
Djangoではdjango.contrib.auth
という強力なauth機能がありますが、今回はAPI実装なので簡単にはいきませんでした
API用のライブラリdjango-rest-authがあったので、こちらを使って進めていきます
パスワードリセットの流れ
ユーザ
- トップページからパスワードリセット画面に遷移
- メールアドレス入力・送信
- URLを含んだメール受信
- パスワード入力・送信
API
- メールアドレスを含むリクエスト受信
- リセット画面へのURLを含むメール送信
- パスワードを含むリクエスト受信
の流れになります。よくある流れです
django.contrib.auth
をそのまま使うならテンプレートを設定するだけでほぼできあがるのですが、今回APIのエンドポイント作成になるので、ライブラリを利用していきます
django-rest-authとは
Documentationによると、ユーザ登録、ログインログアウト、パスワード変更やリセットまでを行ってくれるライブラリのようです
便利だけどドキュメントが絶妙に不親切だったので、ソースコード読みながら実装しました
ひとまず
pip install django-rest-auth
django-rest-authにおける実装
API
ライブラリにまかせておくなら、urls.py
に次の2行を追加すれば動きます
url(r'^rest-auth/', include('rest_auth.urls')),
url(r'^', include('django.contrib.auth.urls')),
このままだとメールのテンプレートが設定できません
テンプレートの設定を行わないと、メールに記載されるURLのドメインの設定ができませんでした
設定するためにはdjango-rest-authのserializerをOverrideする必要があるようです
get_email_options
でemail_template_name
などを指定します。他にも設定できる項目はあるようです
from rest_auth.serializers import PasswordResetSerializer
class CustomPasswordResetSerializer(PasswordResetSerializer):
def get_email_options(self):
data = {
'email_template_name': 'email/password_reset.html',
'subject_template_name': 'email/password_reset_subject.txt',
}
return data
Emailテンプレートはdjangoのデフォルトのテンプレートがあるので、これを参考にしてみてください
{{ protocol }}://{{ domain }}
でフロントのURLに誘導してください
{% load i18n %}{% autoescape off %}
{% blocktrans %}You're receiving this email because you requested a password reset for your user account at {{ site_name }}.{% endblocktrans %}
{% trans "Please go to the following page and choose a new password:" %}
{% block reset_link %}
{{ protocol }}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %}
{% endblock %}
{% trans "Your username, in case you've forgotten:" %} {{ user.get_username }}
{% trans "Thanks for using our site!" %}
{% blocktrans %}The {{ site_name }} team{% endblocktrans %}
{% endautoescape %}
そしてviews.py
もこれに合わせて調整します
ここを参考にしました
from rest_framework.generics import GenericAPIView
from user.serializers import CustomPasswordResetSerializer
class PasswordResetView(GenericAPIView):
permission_classes = (permissions.AllowAny,)
serializer_class = CustomPasswordResetSerializer
def post(self, request):
serializer = self.get_serializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response('Password reset e-mail has been sent.', status=200)
return Response(serializer.errors, status=400)
最後にurls.py
でこのviewのルーティングを行います(1行目)
url(r'^/password/reset/$', PasswordResetView.as_view()),
url(r'^rest-auth/', include('rest_auth.urls')),
url(r'^', include('django.contrib.auth.urls')),
これで2つのAPIができました
-
/password/reset/
: メールアドレス送信用 -
/rest-auth/password/reset/confirm/
: パスワード送信用
パスワード送信用はdjango-rest-auth
にまかせて問題ないと思います
フロント
フロント側で準備するのは下の2つのようです(他に遷移元・先なども準備してください)
ビューで登録させる内容と、APIは次の通りです
メールアドレスを入力するビュー
メールアドレスを入力するフォームを作成します
/password/reset/
パスワードを入力するビュー
パスワードを入力するフォームを確認用も含めて2つ作成します
/rest-auth/password/reset/confirm/
- new_password1
- new_password2
- uid
- token
uidとtokenはメールに記載されたURLに含まれている文字列を指定します。
https://***/reset/{ uid }/{ token }/
まとめ
かなり強引な方法の気がします
もっと良い方法がないかなあと考えてはいますが