このサイトは、只今WEB業界で活躍中のデザイナー、プログラマーの方々の情報を集めたweb統合情報サイトです。

Archives Details

Django django-rest-auth + Nuxt.js auth-module で作る SPA JWT OAuth ログインシステム その2

Python

2020.06.08

どもです。

前回の「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
  • email

ここでは、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
  • email
  • 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

oauth-social-login-checker

上記のツールなどを利用し、「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

https://github.com/webcyou-org/simple-rest-login-server

Comment

Related Article

Django (DRF)で、ユーザーのモデルを作成時にハッシュidを別で保存する。

2020.09.10

macOS pyenv環境でtkinterが動かないので、再度インストール

2020.08.09

Django django-rest-auth + Nuxt.js auth-module で作る SPA JWT OAuth ログインシステム その3

2020.06.14

Django django-rest-auth + Nuxt.js auth-module で作る SPA JWT OAuth ログインシステム その2

2020.06.08

Django django-rest-auth + Nuxt.js auth-module で作る SPA JWT OAuth ログインシステム その1

2020.06.07

Django django-allauthで、サクッとソーシャルログイン機能を実装

2020.04.12

PythonでGUIアプリ開発「PyQt」を使った感想

2019.07.18

CATEGORY LIST

LATEST NEWS

Maker Faire Tokyo 2020に行ってきました。

イベント

2020.10.06

パックマン 解析プログラム動画から見る 追跡アルゴリズム

AI・Bot・algorithm

2020.09.23

Django (DRF)で、ユーザーのモデルを作成時にハッシュidを別で保存する。

Python

2020.09.10

ゲオのサマーセール 980円以下のゲームソフトが半額!8月16日(日)まで。で購入したもの。

Game

2020.08.13

Webassembly用いて、SDL 2.0をブラウザでレンダリング

C++

2020.08.10

macOS pyenv環境でtkinterが動かないので、再度インストール

Python

2020.08.09

Mac Home brewでSDL2.0を簡単に環境設定

C

2020.06.24

Django django-rest-auth + Nuxt.js auth-module で作る SPA JWT OAuth ログインシステム その3

Python

2020.06.14

Django django-rest-auth + Nuxt.js auth-module で作る SPA JWT OAuth ログインシステム その2

Python

2020.06.08

Django django-rest-auth + Nuxt.js auth-module で作る SPA JWT OAuth ログインシステム その1

Python

2020.06.07

このコロナ禍で、飛沫感染防止など求められる中「電子メモパッド」が重宝。 1300円で購入可能な電子メモパッドが超絶便利な件。

tool

2020.06.02

OAuthのフローを可視化できるツールを作ってみました。

JavaScript

2020.05.17

RANKING

Follow

SPONSOR

現在、掲載募集中です。



Links

About Us

WEBデザイナーの、WEBデザイナーによる、WEBデザイナーの為のサイト。「みんなで書こう!」と仲間を募ってみたが、結局書くのは自分だけとなってしまいました。日々のメモを綴っていきます。

Entry Profile

Graphical FrontEnd Engineer
- Daisuke Takayama

MAD CITY 北九州市で生まれ育つ。20代はバンド活動に明け暮れ、ふと「webデザイナーになりたい。」と思い、デジタルハリウッド福岡校入学。卒業後、数々の賞を受賞、web業界をざわつかせる。
現在、主に、ゲーム制作中心に港区六本木界隈で活動中。

FOLLOW US