LoginSignup
10
11

More than 5 years have passed since last update.

Django rest frameworkを使ったAPI経由のパスワードリセットを行う方法

Posted at

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行を追加すれば動きます

urls.py
url(r'^rest-auth/', include('rest_auth.urls')),
url(r'^', include('django.contrib.auth.urls')),

このままだとメールのテンプレートが設定できません
テンプレートの設定を行わないと、メールに記載されるURLのドメインの設定ができませんでした

設定するためにはdjango-rest-authのserializerをOverrideする必要があるようです
get_email_optionsemail_template_nameなどを指定します。他にも設定できる項目はあるようです

serializers.py
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に誘導してください

django/django/contrib/admin/templates/registration/password_reset_email.html
{% 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もこれに合わせて調整します
ここを参考にしました

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行目)

urls.py
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/

  • email

パスワードを入力するビュー

パスワードを入力するフォームを確認用も含めて2つ作成します

/rest-auth/password/reset/confirm/

  • new_password1
  • new_password2
  • uid
  • token

uidとtokenはメールに記載されたURLに含まれている文字列を指定します。

https://***/reset/{ uid }/{ token }/

まとめ

かなり強引な方法の気がします
もっと良い方法がないかなあと考えてはいますが

10
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
11