DjangoアプリにGoogleでログインできるようにする

こんにちは。今日はDjangoアプリにGoogleなどのソーシャルメディアアカウントを使ってログインする方法を紹介します。

今回使用するdjango-allauthライブラリを使用することにより、ソーシャルメディアでユーザーをサインアップさせることができます。

詳しいドキュメンテーションはこちらを参照してください。(英語ですが。)

完成したサンプルコードはこちら(GitHub)を参照してください。

あと、Googleクラウドのサービスは課金されるものになるのでテストが完了したら課金はオフにしておきましょう。(最初に登録した場合は無料のクレジットがもらえるはずですが。)

概要

DjangoはPythonのウェブフレームワークで、データベースとの連携からAPIの作成まですべてカバーできる素晴らしいフレームワークです。今回のDjangoアプリのAuthentication(認証)はDjangoアプリであり、DjangoのREST APIを使ったものではありません。今後APIを使ったソーシャルメディアのログインの仕方も紹介したいのでご期待を!

今日の環境

django-allauthライブラリ

django-allauthはGitHubで7.8kのスターが付いている中レベルの人気のライブラリです。Django自体が69.1kのスターでDjangoの中でも人気のあるライブラリといえるでしょうか。

django-allauthライブラリの特徴を上げます。

  • ローカルのアカウントとソーシャルメディアのアカウントを同時にサインアップさせることができます。
  • ローカルアカウントに一つ以上のソーシャルメディアのアカウントを接続させることができます。
  • ソーシャルアカウントを外したい場合でローカルのアカウントを残したい場合はローカル用のパスワードを設定する必要があります。
  • ソーシャルアカウント(Googleなど)を使って即座にサインアップできます。
  • メールアドレスの管理ができる(複数の登録、メインのメールアドレスの登録)
  • パスワードを忘れた場合のフローがあります。
  • メールアドレスの証明のフローがあります。

2023年の3月時点でサポートされているプロバイダーです。記事の下の方にすべて記載したものがあります。

  • Amazon (OAuth2)
  • AngelList (OAuth2)
  • Bitly (OAuth2)
  • Dropbox (OAuth)
  • Facebook (both OAuth2 and JS SDK)
  • Feedly (OAuth2)
  • Github (OAuth2)
  • Google (OAuth2)
  • Instagram (OAuth2)
  • LinkedIn (OAuth, OAuth2)
  • OpenId
  • Paypal (OAuth2)
  • Persona
  • SoundCloud (OAuth2)
  • Stack Exchange (OAuth2)
  • Twitch (OAuth2)
  • Twitter (OAuth)
  • Vimeo (OAuth)
  • VK (OAuth2)
  • Weibo (OAuth2)など。。。
  • (yahooなども追加されています。)

知っておきたい細かい特徴

  • 複数のログイン方法をサポートできます。(メールアドレス、ユーザーネームでの認証)
  • アカウントの証明方法をなし、もしくはメールアドレスで確認する方法がサポートされています。
  • アクセストークンはデータベースに保管されます。
  • 追加でサインアップの際に質問を追加することができます。
  • Facebook, Twitterなどのライクはデータベースでコンフィグできます。(SocialApp モデルを参照)

Djangoアプリの作成

では、下記のコマンドを使ってDjangoアプリをセットアップしていきます。

#オプショナルで仮想環境を作成
pip install virtualenv
virtualenv env
source env/bin/activate
#WIndows
env\Scripts\activate

python -m pip install Django
django-admin startproject authapp
#authappは自分でつけたアプリ名です。

アプリができたらアプリのディレクトリに移動し、データベースを作成します。

そのままスーパーユーザー(アドミンのアカウント)を作成しましょう。

cd authapp/
python manage.py migrate

python manage.py createsuperuser

python manage.py runserver

では、最後のrunserverのコマンドでテストサーバーを起動し、ブラウザからアクセスしてみましょう。

問題がなければCtl+Cで一旦サーバーを止めて、次に進みます。

Django-allauthのインストール

次にメインのdjango-allauthのライブラリをインストールします。

pip install django-allauth

では、インストールができたらテキストエディタを開いてsettings.pyを編集していきます。

まずは変数TEMPLATESを見てください。これはいじる必要はありませんが、allauthがこの’django.template.context_processors.request’を使うことだけ知っておきましょう。

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                # Already defined Django-related contexts here

                # `allauth` needs this from django
                'django.template.context_processors.request',
            ],
        },
    },
]

つぎにAUTHENTICATION_BACKENDS変数を追加します。

AUTHENTICATION_BACKENDS = [
    # Needed to login by username in Django admin, regardless of `allauth`
    'django.contrib.auth.backends.ModelBackend',

    # `allauth` specific authentication methods, such as login by e-mail
    'allauth.account.auth_backends.AuthenticationBackend',
]

‘django.contrib.auth.backends.ModelBackend’はDjangoのローカルアカウントでログインするための認証を設定するものです。

‘allauth.account.auth_backends.AuthenticationBackend’はallauthで使う方です。

次にINSTALLED_APPSを見てください。

ここにライブラリのアプリと、必要なソーシャルメディアのアプリを登録します。不要なものは外してください。

INSTALLED_APPS = [
    ...
    # The following apps are required:
    'django.contrib.auth',
    'django.contrib.messages',
    'django.contrib.sites',

    'allauth',
    'allauth.account',
    'allauth.socialaccount',
    # ... include the providers you want to enable:
    'allauth.socialaccount.providers.agave',
    'allauth.socialaccount.providers.amazon',
    'allauth.socialaccount.providers.amazon_cognito',
    'allauth.socialaccount.providers.angellist',
    'allauth.socialaccount.providers.apple',
    'allauth.socialaccount.providers.asana',
    'allauth.socialaccount.providers.auth0',
    'allauth.socialaccount.providers.authentiq',
    'allauth.socialaccount.providers.azure',
    'allauth.socialaccount.providers.baidu',
    'allauth.socialaccount.providers.basecamp',
    'allauth.socialaccount.providers.battlenet',
    'allauth.socialaccount.providers.bitbucket',
    'allauth.socialaccount.providers.bitbucket_oauth2',
    'allauth.socialaccount.providers.bitly',
    'allauth.socialaccount.providers.box',
    'allauth.socialaccount.providers.cern',
    'allauth.socialaccount.providers.cilogon',
    'allauth.socialaccount.providers.clever',
    'allauth.socialaccount.providers.coinbase',
    'allauth.socialaccount.providers.dataporten',
    'allauth.socialaccount.providers.daum',
    'allauth.socialaccount.providers.digitalocean',
    'allauth.socialaccount.providers.dingtalk',
    'allauth.socialaccount.providers.discord',
    'allauth.socialaccount.providers.disqus',
    'allauth.socialaccount.providers.douban',
    'allauth.socialaccount.providers.doximity',
    'allauth.socialaccount.providers.draugiem',
    'allauth.socialaccount.providers.drip',
    'allauth.socialaccount.providers.dropbox',
    'allauth.socialaccount.providers.dwolla',
    'allauth.socialaccount.providers.edmodo',
    'allauth.socialaccount.providers.edx',
    'allauth.socialaccount.providers.eventbrite',
    'allauth.socialaccount.providers.eveonline',
    'allauth.socialaccount.providers.evernote',
    'allauth.socialaccount.providers.exist',
    'allauth.socialaccount.providers.facebook',
    'allauth.socialaccount.providers.feedly',
    'allauth.socialaccount.providers.figma',
    'allauth.socialaccount.providers.fivehundredpx',
    'allauth.socialaccount.providers.flickr',
    'allauth.socialaccount.providers.foursquare',
    'allauth.socialaccount.providers.frontier',
    'allauth.socialaccount.providers.fxa',
    'allauth.socialaccount.providers.gitea',
    'allauth.socialaccount.providers.github',
    'allauth.socialaccount.providers.gitlab',
    'allauth.socialaccount.providers.globus',
    'allauth.socialaccount.providers.google',
    'allauth.socialaccount.providers.gumroad',
    'allauth.socialaccount.providers.hubic',
    'allauth.socialaccount.providers.instagram',
    'allauth.socialaccount.providers.jupyterhub',
    'allauth.socialaccount.providers.kakao',
    'allauth.socialaccount.providers.keycloak',
    'allauth.socialaccount.providers.lemonldap',
    'allauth.socialaccount.providers.line',
    'allauth.socialaccount.providers.linkedin',
    'allauth.socialaccount.providers.linkedin_oauth2',
    'allauth.socialaccount.providers.mailchimp',
    'allauth.socialaccount.providers.mailru',
    'allauth.socialaccount.providers.mediawiki',
    'allauth.socialaccount.providers.meetup',
    'allauth.socialaccount.providers.microsoft',
    'allauth.socialaccount.providers.naver',
    'allauth.socialaccount.providers.nextcloud',
    'allauth.socialaccount.providers.odnoklassniki',
    'allauth.socialaccount.providers.openid',
    'allauth.socialaccount.providers.openid_connect',
    'allauth.socialaccount.providers.openstreetmap',
    'allauth.socialaccount.providers.orcid',
    'allauth.socialaccount.providers.patreon',
    'allauth.socialaccount.providers.paypal',
    'allauth.socialaccount.providers.persona',
    'allauth.socialaccount.providers.pinterest',
    'allauth.socialaccount.providers.pocket',
    'allauth.socialaccount.providers.quickbooks',
    'allauth.socialaccount.providers.reddit',
    'allauth.socialaccount.providers.robinhood',
    'allauth.socialaccount.providers.salesforce',
    'allauth.socialaccount.providers.sharefile',
    'allauth.socialaccount.providers.shopify',
    'allauth.socialaccount.providers.slack',
    'allauth.socialaccount.providers.snapchat',
    'allauth.socialaccount.providers.soundcloud',
    'allauth.socialaccount.providers.spotify',
    'allauth.socialaccount.providers.stackexchange',
    'allauth.socialaccount.providers.steam',
    'allauth.socialaccount.providers.stocktwits',
    'allauth.socialaccount.providers.strava',
    'allauth.socialaccount.providers.stripe',
    'allauth.socialaccount.providers.telegram',
    'allauth.socialaccount.providers.trainingpeaks',
    'allauth.socialaccount.providers.trello',
    'allauth.socialaccount.providers.tumblr',
    'allauth.socialaccount.providers.twentythreeandme',
    'allauth.socialaccount.providers.twitch',
    'allauth.socialaccount.providers.twitter',
    'allauth.socialaccount.providers.twitter_oauth2',
    'allauth.socialaccount.providers.untappd',
    'allauth.socialaccount.providers.vimeo',
    'allauth.socialaccount.providers.vimeo_oauth2',
    'allauth.socialaccount.providers.vk',
    'allauth.socialaccount.providers.wahoo',
    'allauth.socialaccount.providers.weibo',
    'allauth.socialaccount.providers.weixin',
    'allauth.socialaccount.providers.windowslive',
    'allauth.socialaccount.providers.xing',
    'allauth.socialaccount.providers.yahoo',
    'allauth.socialaccount.providers.yandex',
    'allauth.socialaccount.providers.ynab',
    'allauth.socialaccount.providers.zoho',
    'allauth.socialaccount.providers.zoom',
    'allauth.socialaccount.providers.okta',
    'allauth.socialaccount.providers.feishu',
    ...
]

次にSITE_IDを追加します。

SITE_ID = 1

最後にオプショナルでソーシャルメディアのプロバイダーごとで細かい設定を追加することができます。

今回は省きますが参考のコードはこれになります。

# Provider specific settings
SOCIALACCOUNT_PROVIDERS = {
    'google': {
        # For each OAuth based provider, either add a ``SocialApp``
        # (``socialaccount`` app) containing the required client
        # credentials, or list them here:
        'APP': {
            'client_id': '123',
            'secret': '456',
            'key': ''
        }
    }
}

次にurls.pyにaccountsのURLを追加します。

from django.urls import path, include

urlpatterns = [
    ...
    path('accounts/', include('allauth.urls')),
    ...
]

これにより下記のことができるようになります。

django.contrib.auth.urlsが提供するローカルのDjangoアカウントで使えるloginlogoutのURLは使用する必要はありません。

代わりに、allauthのライブラリが提供するaccount_loginaccount_logoutaccount_set_passwordのURLが使えるようになります。

ではインストールが完了したらallauthで使用するデータベースのテーブルを作ります。

python manage.py migrate

#サーバーを起動します。
python manage.py runserver

使えるURL

では、allauthのライブラリですでに設定されているURLを見てみます。ブラウザからhttp://127.0.0.1:8000/accounts/にアクセスしてみましょう。accounts/はdjango-allauthで指定されたURLですね。

そうすると下記のようにデフォルトで使えるURLが確認できます。

admin/
accounts/ signup/ [name='account_signup']
accounts/ login/ [name='account_login']
accounts/ logout/ [name='account_logout']
accounts/ password/change/ [name='account_change_password']
accounts/ password/set/ [name='account_set_password']
accounts/ inactive/ [name='account_inactive']
accounts/ email/ [name='account_email']
accounts/ confirm-email/ [name='account_email_verification_sent']
accounts/ ^confirm-email/(?P<key>[-:\w]+)/$ [name='account_confirm_email']
accounts/ password/reset/ [name='account_reset_password']
accounts/ password/reset/done/ [name='account_reset_password_done']
accounts/ ^password/reset/key/(?P<uidb36>[0-9A-Za-z]+)-(?P<key>.+)/$ [name='account_reset_password_from_key']
accounts/ password/reset/key/done/ [name='account_reset_password_from_key_done']
accounts/ google/

では試しに、http://127.0.0.1:8000/accounts/signup/のURLにアクセスしてみます。

allauthライブラリのコンフィグ

次にdjango-allauthのコンフィグを見てみます。

このようにサインアップのテンプレートが表示されました。

ここでDjangoのアドミンパネルでログインしている人は一旦ログアウトしておきましょう。

ログインしているユーザーがProfileページに誘導されてしまうのでサインアップページにアクセスできません。

(そこまで設定されているのでよいですが。)

Django-allauthのコンフィグ

ここで、Django-allauthのコンフィグのページを見て何ができるか見ておきます。

例えば、サインアップの際にEmailがオプショナルになっているようなので下記の部分をsettings.pyに追加してEmailを必須にすることもできます。

ACCOUNT_EMAIL_REQUIRED=True

そうするとEmailが必須になったことが分かりますね。

Googleのシークレットキー

ではアドミンパネルでSocial applicationsテーブルを見てみます。ここに新しいデータを追加してGoogleのコンフィグを完了させたいです。

では必要なAPIキーをGoogleクラウドから作成しましょう。

https://console.cloud.google.com/

もしGoogleクラウドコンソールに慣れていない人は色々なことができるので試してみてください。(前回紹介したGoogleマップを使ったカスタムマップの記事も参考にしてください。)

今日は簡単に始められる方法を紹介します。

まずは下記のURLにアクセスしてください。

https://developers.google.com/gmail/api/quickstart/js?hl=ja

ここでAPIの有効化のボタンを押します。

次にプロジェクトを確認タブの次へをクリックします。

有効にします。

これで右上のベルのマークを見るをプロジェクトにAPIが追加されたことが表示されます。それをクリックしてください。

そうするとダッシュボードにたどり着きます。

下からは、Googleクラウドから作成する方法を紹介します。

とりあえずは、左のメニューからAPIとサービス→認証情報をクリックします。

では、このページから+認証情報を作成をクリックします。

OAuthクライアントIDを作成します。

質問に従い、認証情報を作成します。

クライアントIDができたらクライアント名をクリックしてクライアントIDをコピーします。

では、コピーしたクライアントIDとシークレットキーをDjangoのSocial ApplicationsのGoogleデータにペーストします。

ちなみにこれらの情報は絶対に他人にシェアしないようにしてください。画像のものは無効化されているので使い物になりません。

Googleでログインのテスト

ではGoogleのアカウントでログインできるかテストしましょう。

http://127.0.0.1:8000/accounts/login/

のURLからGoogleのリンクを開きます。

そうするとおなじみのGoogleのアカウントでログインのページに飛ばされました。

ログインが完了するとhttp://127.0.0.1:8000/accounts/profile/のページにリダイレクトされましたね!

では、DjangoのアドミンパネルでUsersを参照すると先ほどGoogleでログインしたユーザーが追加されていることが証明できました。

ちなみにすべてのログイン、ログアウト、サインアップ、プロフィールなどのテンプレートはDjango-allauthのライブラリの中にあります。実際に作成する場合はtemplatesディレクトリを作成してスタイルがあるものを作成することになるのでそれが次のステップになると思います。

では、お疲れ様です。