kivantium活動日記

プログラムを使っていろいろやります

DjangoでTwitter認証を行う

前回の記事のつづきです。 kivantium.hateblo.jp

今回はTwitterでのOAuth認証を実装して、タイムラインを読み込むところまで進めます。

ライブラリのインストール

認証に必要なsocial-auth-app-djangoというライブラリをインストールします。前回作ったプロジェクトのルートディレクトリで以下を実行します。

pipenv shell
pipenv install social-auth-app-django

Twitterアプリケーションの作成

Twitter Developerのページからアプリを作成して、API keyとAPI secret keyを入手します。他に解説記事がたくさんあると思うので詳しくは書きません。

コールバックURLの追加

Twitterアプリケーションに適切なコールバックURLを設定しないと403エラーが出ます。今回のディレクトリ構成だと、ローカルで実行する場合は

http://localhost:8000/complete/twitter/

Herokuで実行する場合は

https://<appname>.herokuapp.com/complete/twitter/

を追加する必要があります。複数追加できるので両方追加しておくといいと思います。

設定の変更

playground/settings.pyを以下のように変更します。

 (略)

 INSTALLED_APPS = [
     'django.contrib.sessions',
     'django.contrib.messages',
     'django.contrib.staticfiles',
+    'hello',
+    'social_django',
 ]
 
 (略)

 TEMPLATES = [
     {
         'BACKEND': 'django.template.backends.django.DjangoTemplates',
         'DIRS': [],
         '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',
+                'social_django.context_processors.backends',
+                'social_django.context_processors.login_redirect',
             ],
         },
     },
 ]
 
 (略)
 
 AUTH_PASSWORD_VALIDATORS = [
 
 LANGUAGE_CODE = 'en-us'
 
-TIME_ZONE = 'UTC'
+TIME_ZONE = 'Asia/Tokyo'
 
 USE_I18N = True
 
 (略)
 
 STATIC_URL = '/static/'
 
 STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
 
+AUTHENTICATION_BACKENDS = [
+    'social_core.backends.twitter.TwitterOAuth',
+    'django.contrib.auth.backends.ModelBackend',
+]
+
+SOCIAL_AUTH_LOGIN_REDIRECT_URL = '/'
+
 # Use different settings in local environment and Heroku
 # https://qiita.com/miler0528/items/1926e93ed97979f8e9fa

 (略)
 
 if not DEBUG:
     import django_heroku
     django_heroku.settings(locals())
+
+    SOCIAL_AUTH_TWITTER_KEY = os.environ['TWITTER_CONSUMER_KEY']
+    SOCIAL_AUTH_TWITTER_SECRET = os.environ['TWITTER_CONSUMER_SECRET']

playground/local_settings.pyの末尾に以下の内容を追記します。

SOCIAL_AUTH_TWITTER_KEY = 'XXXXXXXXXXXXXXXXXXXXXXXXX'
SOCIAL_AUTH_TWITTER_SECRET = 'XXXXXXXXXXXXXXXXXXXXXXXXX'

TWITTER_CONSUMER_KEYTWITTER_CONSUMER_SECRETには先ほど作成したAPI keyおよびAPI secret keyを入れます。

設定を変更したのでマイグレートを行います。

python manage.py migrate

認証システムの実装

hello/urls.pyを以下のように変更してログアウト画面を追加します。

-from django.urls import path
+from django.urls import path, include
+import django.contrib.auth.views
 
 from . import views
 
 urlpatterns = [
     path('', views.index, name='index'),
+    path('logout/',
+        django.contrib.auth.views.LogoutView.as_view(template_name = 'hello/logout.html'),
+        name='logout'),
 ]

playground/urls.pyに一行追記します。

 urlpatterns = [
     path('', include('hello.urls')),
+    path('', include('social_django.urls', namespace='social')),
 ]

hello/views.pyを次のように変更します。こうすることで、ログインしているかどうかでトップページにアクセスしたときの表示内容を変えることができます。

-from django.http import HttpResponse
-
+from django.shortcuts import render
+from social_django.models import UserSocialAuth
 
 def index(request):
-    return HttpResponse("Hello, world!")
+    if request.user.is_authenticated:
+        user = UserSocialAuth.objects.get(user_id=request.user.id)
+        return render(request,'hello/index.html', {'user': user})
+    else:
+        return render(request,'hello/index.html')

テンプレートの追加

テンプレートを入れるフォルダを作成します。

mkdir -p hello/templates/hello

hello/templates/hello/index.htmlを以下の内容で作成します。

<html>
  <head>
    <title>index</title>
  </head>
  <body>
  {% if request.user.is_authenticated %}
    <p>あなたはログインしています (screen_name: {{ user.access_token.screen_name }})</p>
    <a href="/logout"><button type="button">ログアウト</button></a>
  {% else %}
    <p>あなたはログインしていません</p>
    <button type="button" onclick="location.href='{% url 'social:begin' 'twitter' %}'">Twitterでログイン</button>
  {% endif %}
  </body>
</html>

hello/templates/hello/logout.htmlを以下の内容で作成します。

<html>
  <head>
    <title>ログアウト</title>
  </head>
  <body>
    <p>ログアウトしました</p>
    <p><a href="/"><button type="button">トップへ</button></a></p>
  </body>
</html>

以上の作業の結果、以下のようなディレクトリ構成になります。

playground/
├── Pipfile
├── Pipfile.lock
├── Procfile
├── db.sqlite3
├── hello
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── migrations
│   │   └── __init__.py
│   ├── models.py
│   ├── templates
│   │   └── hello
│   │       ├── index.html
│   │       └── logout.html
│   ├── tests.py
│   ├── urls.py
│   └── views.py
├── manage.py
├── playground
│   ├── __init__.py
│   ├── asgi.py
│   ├── local_settings.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
└── runtime.txt

ローカルでのテスト

python manage.py runserver

ブラウザでhttp://localhost:8000/にアクセスすると、ログインしていない場合の画面が表示されます。

f:id:kivantium:20200412154331p:plain:w250

Twitterでログイン」ボタンを押すとこんな感じの認証画面に遷移します。 f:id:kivantium:20200412153257p:plain:w600

「連携アプリを認証」ボタンを押すと、ログインしてトップ画面に戻ります。ログインしている場合はscreen_nameが表示されます。

f:id:kivantium:20200412154738p:plain:w400

ログアウトボタンを押すとログアウトされます。

f:id:kivantium:20200412154920p:plain:w200

デプロイ

ローカル環境で正しく動いたらHerokuにデプロイします。

heroku config:set TWITTER_CONSUMER_KEY='XXXXXXXXXXXXXXXXXXXXXXXXX'
heroku config:set TWITTER_CONSUMER_SECRET='XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
git add .
git commit -m 'Add login with Twitter'
git push heroku master
heroku run python manage.py migrate

https://<appname>.herokuapp.com/にアクセスすると、ローカルと同じようなアプリが動くはずです。

タイムラインの読み込み

これでTwitter APIを使う準備が整ったので、試しにタイムラインの読み込みを行ってみます。 Twitter APIを扱うライブラリには以前の記事でも取り上げたtweepyを使うことにします。

tweepyのインストール

pipenv install tweepy

hello/views.pyの変更内容

 from django.shortcuts import render
 from social_django.models import UserSocialAuth
+from django.conf import settings
+
+import tweepy
 
 def index(request):
     if request.user.is_authenticated:
         user = UserSocialAuth.objects.get(user_id=request.user.id)
-        return render(request,'hello/index.html', {'user': user})
+        consumer_key = settings.SOCIAL_AUTH_TWITTER_KEY
+        consumer_secret = settings.SOCIAL_AUTH_TWITTER_SECRET
+        access_token = user.extra_data['access_token']['oauth_token']
+        access_secret = user.extra_data['access_token']['oauth_token_secret']
+        auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
+        auth.set_access_token(access_token, access_secret)
+        api = tweepy.API(auth)
+        timeline = api.home_timeline()
+        return render(request,'hello/index.html', {'user': user, 'timeline': timeline})
     else:
         return render(request,'hello/index.html')

/hello/templates/hello/index.htmlの変更内容

   <body>
   {% if request.user.is_authenticated %}
     <p>あなたはログインしています (screen_name: {{ user.access_token.screen_name }})</p>
+    <ul>
+    {% for tweet in timeline %}
+    <li>@{{ tweet.user.screen_name }}: {{ tweet.text }}</li>
+    {% endfor %}
+    </ul>
     <a href="/logout"><button type="button">ログアウト</button></a>
   {% else %}
     <p>あなたはログインしていません</p>

変更をコミットしてgit push heroku masterでデプロイすると、タイムラインから最新20件のツイートを読み込んで表示することができるようになります。

f:id:kivantium:20200412153845p:plain

次回はいつになるか分かりませんが、Twitterを使った何かしらのアプリを作ってみようと思います。

参考文献

広告コーナー