Django django-rest-auth + Nuxt.js auth-module で作る SPA JWT OAuth ログインシステム その2
2020.06.08
この記事は最終更新日から1年以上が経過しています。

どもです。
前回の「Django django-rest-auth + Nuxt.js auth-module で作る SPA JWT OAuth ログインシステム その1」の続きとなります。
DjangoのNuxt.jsもそうですが、公開されているパッケージを活用しつつ、極力記述の少なくすむように構築していきます。
では、早速 Django django-rest-authで OAuthログインできるようにAPIを作成していきましょう。
バージョン
- django 2.2.13
- python 3.7
使用パッケージ
- djangorestframework 3.10.3
- django-allauth
- django-rest-auth
- djangorestframework-jwt
完成ソースを先に見たい方は、こちらから
GitHub
https://github.com/webcyou-org/simple-rest-login-server
環境作成
まずは、ディレクトリ作成します。
$ mkdir simple-rest-login-server && cd simple-rest-login-server
仮想化環境を開始します。
$ python3 -m venv venv $ source venv/bin/activate
pipenv用いて、必要なパッケージのインストールを行います。
$ pipenv install django==2.2.10 djangorestframework==3.10.3 django-allauth django-rest-auth djangorestframework-jwt
アプリケーションと分別できるように、分かりやすいプロジェクト構成にするため、「config」という名前でstartprojectコマンドでモジュール作成します。
startappでは「accounts」の名前のモジュール作成します。
$ django-admin startproject config . $ python manage.py startapp accounts
config/settings修正
INSTALLED_APPS app追加
「config/settings.py」を修正していきます。
まずは、django-allauthパッケージの追加を行っていきます。
config/settings.py
'django.contrib.sites', 'rest_framework', 'rest_framework.authtoken', 'rest_auth', 'rest_auth.registration', 'allauth', 'allauth.account', 'allauth.socialaccount', 'allauth.socialaccount.providers.google'
「allauth.socialaccount.providers.〇〇〇〇」は、利用したいprovider毎に追加する必要があります。
今回は「Google」のみ追加する形で行っていきます。
TEMPLATESの箇所には、以下を追加
'DIRS': [os.path.join(BASE_DIR, 'templates')],
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
AUTHENTICATION_BACKENDS
AUTHENTICATION_BACKENDSは、
「’django.contrib.auth.backends.ModelBackend’」
「’allauth.account.auth_backends.AuthenticationBackend’」を追加していきます。
管理画面はユーザー名/パスワードで認証することが可能となります。
AUTHENTICATION_BACKENDS = ( 'django.contrib.auth.backends.ModelBackend', 'allauth.account.auth_backends.AuthenticationBackend', )
REST_FRAMEWORK
DEFAULT_PERMISSION_CLASSESは「rest_framework.permissions.IsAuthenticated」
DEFAULT_AUTHENTICATION_CLASSESは「rest_framework.authentication.TokenAuthentication」と
「rest_framework_jwt.authentication.JSONWebTokenAuthentication」を指定します。
DEFAULT_AUTHENTICATION_CLASSES の指定は、
「rest_framework_jwt.authentication.JSONWebTokenAuthentication」のみで大丈夫なのかと思っていましたが、
どうもうまく行かず、ハマった結果、「rest_framework.authentication.TokenAuthentication」も指定することによって回避できました。
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
],
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
'rest_framework_jwt.authentication.JSONWebTokenAuthentication'
)
}
アカウント周りの設定
# 1: 認証⽅式を 「メールアドレスとパスワード」 に変更しユーザー名は使⽤しない設定にします。
# 2: django-allauthに必要記述
# 3: セッションでのログイン無効化、cors origin 有効化
# 4: ユーザー登録時にメールアドレス確認。ユーザー登録画面でにEmailを必須項目
# 5: JWTでの認証ができるようと、トークンの有効期限の設定(開発環境なので取り敢えず期限なし)
# 1
ACCOUNT_AUTHENTICATION_METHOD = 'email'
ACCOUNT_USERNAME_REQUIRED = False
# 2
SITE_ID = 1
# 3
REST_SESSION_LOGIN = False
CORS_ORIGIN_ALLOW_ALL = True
# 4
ACCOUNT_EMAIL_VARIFICATION = 'mandatory'
ACCOUNT_EMAIL_REQUIRED = True
# 5
REST_USE_JWT = True
JWT_AUTH = {
'JWT_VERIFY_EXPIRATION': False,
}
メールアドレス確認の為、メール配信を行うのですが、設定が手間なのでとりあえず開発中はコンソールに表示させる設定にしておきます。
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
その他
言語とタイムゾーンを日本に設定しておきます。
LANGUAGE_CODE = 'ja' TIME_ZONE = 'Asia/Tokyo'
一旦、すべてのホストを 有効化します。(本番で利用の際は制限してください)
ALLOWED_HOSTS = ['*']
データベーステーブル作成(migrate実行)
$ python manage.py migrate
問題がなければ、テーブルが作成されます。
管理者ユーザー作成
管理画面を利用できる管理者ユーザー (スーパーユーザー) を作成します。
管理コマンドは次の通りです。
$ python manage.py createsuperuser
$ python manage.py createsuperuser Username (leave blank to use 'user1'): admin Email address: admin@webcyou.com Password: # パスワードを入力 Password (again): This password is too common. # パスワードが単純過ぎる場合はエラー発生 Password: Password (again): Superuser created successfully.
ここまで、完了したらソーシャル連携認証を行うため、各サービスでのOAuth アプリケーションを登録していきます。
Googleのソーシャルログイン設定
今回は「Google」でログインを行いますので、サービスにて OAuth アプリケーションを登録していきます。
こちらの登録の解説は前回の「Django django-allauthで、サクッとソーシャルログイン機能を実装」でも説明しておりますので、割愛させていただきます。
登録方法がわからない場合は、こちらを参照していただければと思います。
socialaccount用のviews作成
必要最低限のソーシャルログイン用のviewsを作成します。
accounts/views.py
from allauth.socialaccount.providers.google.views import GoogleOAuth2Adapter
from allauth.socialaccount.providers.oauth2.client import OAuth2Client
from rest_auth.registration.views import SocialLoginView
from rest_framework.authentication import SessionAuthentication, BasicAuthentication
class CsrfExemptSessionAuthentication(SessionAuthentication):
def enforce_csrf(self, request):
return
class GoogleLogin(SocialLoginView):
adapter_class = GoogleOAuth2Adapter
callback_url = 'http://127.0.0.1/callback'
client_class = OAuth2Client
authentication_classes = (CsrfExemptSessionAuthentication, BasicAuthentication)
GoogleLogin classを作成します。
adapter_classには、allauth.socialaccount.providers.google.viewsからimportしたGoogleOAuth2Adapterを設定。
callback_urlは任意のURLを設定。
client_classには、allauth.socialaccount.providers.oauth2.clientからimportしたOAuth2Clientを設定。
そのままだと、APIでのリクエストに対して、CSRFトークンがない為エラーになってしまうので、それを回避するためCsrfExemptSessionAuthentication classを作成し、スルーするようにします。
urlルーティング設定
urlsには、以下の項目を追加します。
config/urls.py
from django.conf.urls import include, url
from accounts.views import GoogleLogin
path('api-auth/', include('rest_framework.urls')),
url(r'^rest-auth/', include('rest_auth.urls')),
url(r'^rest-auth/registration/', include('rest_auth.registration.urls')),
url(r'^rest-auth/google/$', GoogleLogin.as_view(), name='google_login'),
api-auth/は、ログイン後のAPIとして設定しておきます。
rest-auth/は、include(‘rest_auth.urls’)で、django-rest-authのurlを設定しているのですが、registrationは別の指定しないといけないぽい(django-rest-authのdemoをみると)ので指定してます。
また、ソーシャルアカウント毎に指定も必要となってきますので、作成したviewsを呼び出す様に指定します。
APIでのログインテスト
ここまで、修正が完了しましたら一通り実行できるかと思いますので、Postmanなどを使ってAPIのテストを行っていきましょう。
まずは、ユーザー登録から実行していきます。
django-rest-authのAPIドキュメントを見ていきます。
https://django-rest-auth.readthedocs.io/en/latest/api_endpoints.html
ユーザー登録
APIエンドポイント: /rest-auth/registration/(POST)
params:
- username
- password1
- password2
ここでは、usernameを必要とされていますが、settingで必須項目としては外したので「email」「password1」「password2」の項目をリクエストします。
localhost:8000ポートで起動しているので、URLは以下の様になります。
以下の様に送信すると、レスポンスでtokenのvalueでJWTトークンが返却されるのと、user情報が返却されているのが確認できました。

django adminで、(http://localhost:8000/admin)
で、ユーザーを確認すると新規でユーザーが作成されているのが確認できます。

また、コンソールを確認すると、送信するメールの内容が表示されるが確認できます。
メールを確認すると、以下の様にURLが発行されているのが確認できます。
http://localhost:8000/rest-auth/registration/account-confirm-email/{activation-key}/
発行されたactivation-keyを使い、アカウントのアクティベート(emailの確認)を実行します。
アカウントのアクティベート
APIエンドポイント: /rest-auth/registration/verify-email/ (POST)
params:
- key

emailの存在確認ができ、アカウントをアクティベートすることができます。
現状、アクティベートせずにログインできますが、本番運用などの際は、設定を行ったほうが良いでしょう。
ログイン
それでは、登録されたアカウントでログインを試みて見ましょう。
APIエンドポイント: /rest-auth/login/ (POST)
params:
- username
- password
Returns Token key
ここでも、usernameが必要項目となっておりますが、emailとpasswordでログインしたかったので、setting.pyでusernameを外しているので、「email」「password」を送信しログインを行います。

ログアウトに関しては、SPAによるJWT認証の形となりますので、ログイン状態のセッションを保持しない(参照しない)フローとなりますので、クライアント側でのログアウトとなりますので、特別送信する必要がありません。
APIエンドポイント: /rest-auth/logout/ (POST)
ユーザー情報取得
UserInfoのエンドポイントは以下のとおりとなります。
APIエンドポイント: /rest-auth/user/ (GET, PUT, PATCH)
params:
- username
- first_name
- last_name
Returns pk, username, email, first_name, last_name
こちらは、リクエストbodyでのパラメータは必要とされません。
リクエストヘッダーのAuthorizationにJWTトークンを付与して送信するとユーザー情報が返却されます。
django-rest-authでのAuthorizationのValueパラメータは、「JWT JWT-Token」となります。
「JWT」の後に、半角スペース。その後に、ログイン成功後のレスポンスに含まれるtokenの JWTトークンを付与してリクエストする形となります。

ログイン後のJWTトークンをリクエストヘッダーのAuthorizationに付与して送信するとユーザー情報が返却されるのが確認できました。

ログイン後は、このJWTトークンをクライアント側で保持しセキュアなリクエストの際は、リクエストヘッダーに付与しリクエストすることとなります。
JWTトークンの期限が切れた場合、リメンバートークンを送信するのですが、今回は開発環境という事で、省略し期限なしとしております。
その他、パスワードリセットなどがありますが、そちらも今回割愛させていただきます。(またの機会にでも)
ソーシャルログインのテスト
と、最低限メールでのアカウント登録、ログインなどの確認ができましたので、続いてソーシャルアカウントでの登録、ログインを試して行きたいと思います。
ソーシャルアカウントのAPIエンドポイントは以下のとおりとなります。
APIエンドポイント: /rest-auth/google/ (POST)
params:
- access_token
- code
facebookも「/rest-auth/facebook/ (POST)」とエンドポイントだけ異なり、パラメータは「access_token, code」となります。
ここで、前回の「ハイブリッドフロー」を思い出して欲しいのですが、response_typeに「code token」を設定し「ハイブリッドフロー」を行う予定だったと思います。
なので、ソーシャルログインを開始する際に、response_typeに「code token」を設定しレスポンスに、codeとaccess_tokenを受け取ります。
前回紹介させて頂いた「OAuth Social Login Checker」などを利用すれば、容易に取得する事ができます。
GitHub
上記のツールなどを利用し、「code」と「access_token」を取得し、以下の様にソーシャルログイン用のエンドポイントに送信します。

すると、メールでの登録、ログイン同様にレスポンスに「token」と「user」が返却されているのが確認できます。
アカウントが登録されていない場合は、新規作成。既にある場合はログインという形になります。
(レスポンスはどちらも上記の形となる。)
メールでの登録同様にUserInfoのエンドポイントを叩いてみます。
APIエンドポイント: /rest-auth/user/ (GET, PUT, PATCH)
- username
- first_name
- last_name
Returns pk, username, email, first_name, last_name
すると、メールでの登録同様にユーザー情報の取得が行えているのが確認できたかと思います。
という訳で、メールアドレスでの登録、ログイン、ソーシャルログインがREST APIのJWTトークンで行えたのが確認できましたので、次回はフロント側を実装していきたいと思います。
続きは次回に。
ではではぁ。
Django django-rest-auth + Nuxt.js auth-module で作る SPA JWT OAuth ログインシステム その3

GitHub














