このサイトは、只今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

Go言語でAssetsもシングルバイナリ化に便利な packr パッケージ

go

2020.11.25

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

RANKING

Follow

SPONSOR

現在、掲載募集中です。



Links

About Us

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

Entry Profile

Graphical FrontEnd Engineer
- Daisuke Takayama

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

FOLLOW US