Django の Google OAuth2 ログイン認証

# はじめに
先日 Django2 を用いた Web アプリケーション開発において、Google OAuth2 で Google アカウントを用いて認証を行いました。しかし、いろいろ調べた結果日本語のドキュメントがなかったので、共有しておきます！なお、こちらの内容は（既にお亡くなりになった）海外記事の内容からとってきており、そこからディレクトリ構成などを追記しています。


- 環境
    - Python3.6.4
    - Django2.0.5


# 準備

プロジェクトを作成します。ディレクトリ構成は以下を想定しています。プロジェクト名は gtest としています。

>gtest/
┣ gtest/
┃ ┣ \_\_init\_\_.py
┃ ┣ settings.py
┃ ┣ urls.py
┃ ┣ wsgi.py
┣ templates/
┗ manage.py

以下のコマンドを入力します。

```
$ pip install social-auth-app-django
```

social-auth-app-django は Django で social-auth を行うサードパーティーのパッケージアプリです。Googleアカウント認証やGithubアカウント認証などをサポートしています。これによって、アクセストークンの取得や管理が可能となります。

# コーディング

外部のアプリをインストールしたことを報告してやります。

```python:settings.py
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'social_django' # <- Here,
    ....    
]
```

social_django に既にマイグレーションがあるので、そのままマイグレート。

```
$ python manage.py migrate

Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions, social_django
Running migrations:
  Applying social_django.0001_initial... OK
  Applying social_django.0002_add_related_name... OK
  Applying social_django.0003_alter_email_max_length... OK
  Applying social_django.0004_auto_20160423_0400... OK
  Applying social_django.0005_auto_20160727_2333... OK
  Applying social_django.0006_partial... OK
  Applying social_django.0007_code_timestamp... OK
  Applying social_django.0008_partial_timestamp... OK
```

以上で、必要なデータベースが構築されました。次に設定周りを追加していきます。

```python:settings.py
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',
                'social_django.context_processors.backends',  # <- Here
                'social_django.context_processors.login_redirect', # <- Here
            ],
        },
    },
]
```

**context_processors** に下の２行を追加します。次に、**AUTHENTICATION_BACKENDS** を新たに追記します。

```python:settings.py
AUTHENTICATION_BACKENDS = (
 'social_core.backends.open_id.OpenIdAuth',  # for Google authentication
 'social_core.backends.google.GoogleOpenId',  # for Google authentication
 'social_core.backends.google.GoogleOAuth2',  # for Google authentication
 'social_core.backends.github.GithubOAuth2',  # for Github authentication
 'social_core.backends.facebook.FacebookOAuth2',  # for Facebook authentication
 
 'django.contrib.auth.backends.ModelBackend',
)
```

全ての認証バックエンドを書いていますが、ここでは GoogleOAuth2 のみで十分だと思います。必要に応じて削除してください。ただし、最後の行の **'django.contrib.auth.backends.ModelBackend',** は必須です。

urls.py を次のように更新します。

```python:urls.py
from django.conf.urls import include
from django.urls import path
from django.contrib import admin
from django.contrib.auth import views
from .views import home

urlpatterns = [
    path('admin/', admin.site.urls),
    path('login/', views.login, name='login'),
    path('logout/', views.logout, name='logout'),
    path('auth/', include('social_django.urls', namespace='social')),  # <- Here
    path('', home, name='home'),
]
```

今回は home, login, logout ページを作成します。最初に home にアクセスし、home から login にリンクを貼ります。login 後はリダイレクトで home に戻りますが、最初の home とは違い、取得したユーザーネームと logout へのリンクを貼りつけます。logout へアクセスするとログアウトが完了します。このように、超簡単な動的ページを作成します。

上で説明したログインと、ログイン後のリダイレクト先を追記します。

```python:settings.py
LOGIN_URL = 'login'
LOGIN_REDIRECT_URL = 'home'
```

ここのURLは、urls.py で設定した name と一致させてください。

では、Google アカウント連携に必要なクライアントIDとシークレットキーを入手します。

1. [Google API Console](https://console.developers.google.com/apis/library?project=_) へ移動します。
2. [作成]ボタンを押してプロジェクトを作成してください。
3. プロジェクト名は好きなプロジェクト名で良いですが、ここでは「My Project」とします。
4. [作成]ボタンを押します。
5. ライブラリ選択画面にて、Google+ API を選択
6. [有効にする]を押します。
7. [左上の三本線のナビゲーションメニュー]->[APIとサービス]->[認証情報]をクリックします。
8. OAuth同意画面タブにて、ユーザーに表示するサービス名を記入します。ここでは「My Service」とし、[保存]を押します。
9. 認証情報タブにて、[認証情報を作成]->[OAuthクライアントID]を押します。
1. [ウェブアプリケーション]を押した後、名前は適当で構いませんが、ここでは「My Client」とし、承認済みのリダイレクトURIを次のように2つ設定し、[作成]を押します。

```
http://localhost:8000/auth/complete/google-oauth2/
http://127.0.0.1:8000/auth/complete/google-oauth2/
```

注意点として、本番環境の場合はドメイン部分を変えてください。また、基本的に Django ではプロジェクト内で複数のアプリを実装させることを前提としていますが、複数のアプリ間で同じ認証情報を使うことが多いので、http://(Domain name)/(App name)/auth/ ではなく、http://(Domain name)/auth/ となっています。もし、アプリの中に認証情報を設定したい場合は URL が異なってきますので、重ねてご注意ください。ここはURIミスマッチエラーの報告が非常に多い地帯となっております。ちなみに、私の環境では1行目の localhost を名前解決してくれない？のか、ミスマッチエラーが起きます。2行目の方でうまくいっている状態です。

上記の手順で得られたクライアントIDとクライアントシークレットを settings.py に追加します。認証情報の画面ではクライアントシークレットは表示されていませんが、「My Client」を押せば表示されます。

```python:settings.py
SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = ''  #Paste CLient Key
SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = '' #Paste Secret Key
```

これで設定関連は終わりです。次に見た目部分にいきましょう。

プロジェクト直下に views.py を作成し、home 関数を作成します。

```python:views.py
from django.shortcuts import render


def home(request):
	return render(request, 'home.html')
```

ディレクトリ構成は次の通りです。

>gtest/
┣ gtest/
┃ ┣ \_\_init\_\_.py
┃ ┣ settings.py
┃ ┣ urls.py
┃ ┣ wsgi.py
┃ ┣ views.py
┣ templates/
┗ manage.py

templates 内に home.html を作成します。

```django:home.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Home</title>
</head>
<body>
    {% if user.is_authenticated %}
        <p class="display-4">Hello, {{ user.username }}</p>
        <a class="lead" href="{% url 'logout' %}">Log out</a>
	{% else %}
        <a class="lead" href="{% url 'login' %}">Log in</a>
	{% endif %}
</body>
</html>
```

{% if user.is_authenticated %} でログインしているかどうかを判別します。templates は基本的にオーバーライドするためにあるので、本番環境ではあまり真似しない方がいいと思います。

ディレクトリ構成は次の通りです。

>gtest/
┣ gtest/
┃ ┣ \_\_init\_\_.py
┃ ┣ settings.py
┃ ┣ urls.py
┃ ┣ wsgi.py
┃ ┣ views.py
┣ templates/
┃ ┣ home.html
┗ manage.py

では、次に login.html をオーバーライドします。urls.py にて、django.contrib.auth.views をインポートし、login/ にアクセスされると、views.login が起動します。その中を覗いてみると、registration/login.html をテンプレート名として宣言されているので、templates/ に新たに registration/ ディレクトリを作成し、その中に login.html を作成します。

```django:login.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Login</title>
</head>
<body>
<h2>Login</h2>
  <form method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit">Login</button>
  </form>
   <br>
  <p><strong>-- OR --</strong></p>
  <a href="{% url 'social:begin' 'google-oauth2' %}">Login with Google</a><br>
</body>
</html>
```

念のため、元々のフォームがある状態を想定し、そこに Google 認証を移植する形をとっています。

最後にログアウトページを作成しますが、django.contrib.auth.views の logout を覗くと（ややこしいことに）テンプレート名が registration/logged_out.html となっていたので、オーバーライドするときは logout.html ではなく logged_out.html としてください。

```django:logged_out.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Logout</title>
</head>
<body>
    <p class="display-4">You logged out.</p>
    <a class="lead" href="{% url 'home' %}">Home</a>
</body>
</html>
```

ディレクトリ構成は次の通りです。

>gtest/
┣ gtest/
┃ ┣ \_\_init\_\_.py
┃ ┣ settings.py
┃ ┣ urls.py
┃ ┣ wsgi.py
┃ ┣ views.py
┣ templates/
┃ ┣ registration/
┃ ┃ ┣ login.html
┃ ┃ ┗ logged_out.html
┃ ┗ home.html
┗ manage.py


以上で終わりです。

# デモ

一通り実装できているか確認します。

## デモの流れ

1. home.html：最初のページ。もちろんログインしていないので Login と出ることを確認する。
2. login.html：フォームの下の Login with Google でグーグル認証を行う。
3. home.html：ログイン後、user.is_authenticated が True となり、ユーザー名と Logout リンクが現れることを確認する。
4. logout.html：ログアウト画面を確認する。
5. home.html：ホームに戻り、ログアウトできているかどうかを確認する。



## 結果

以下、順番に画像を貼っていきます。

1. home.html：最初のページ。もちろんログインしていないので Login と出ることを確認する。
![キャプチャ.PNG](https://qiita-image-store.s3.amazonaws.com/0/245406/2f4081a8-b52d-592d-9793-76f7b0fbb347.png)

2. login.html：フォームの下の Login with Google でグーグル認証を行う。
![キャプチャ1.PNG](https://qiita-image-store.s3.amazonaws.com/0/245406/96aa2a41-a586-ded8-0c46-c318f2e5023d.png)

3. home.html：ログイン後、user.is_authenticated が True となり、ユーザー名と Logout リンクが現れることを確認する。
![キャプチャ2.PNG](https://qiita-image-store.s3.amazonaws.com/0/245406/03c7550f-5ba3-ed53-794e-c6b57ddd56a0.png)


4. logout.html：ログアウト画面を確認する。
![キャプチャ3.PNG](https://qiita-image-store.s3.amazonaws.com/0/245406/03297489-9e74-dab8-bfb5-bc0add26af6f.png)

5. home.html：ホームに戻り、ログアウトできているかどうかを確認する。
![キャプチャ.PNG](https://qiita-image-store.s3.amazonaws.com/0/245406/2f4081a8-b52d-592d-9793-76f7b0fbb347.png)


# おわりに

私自身もまだDjango初心者なので間違っている点があるかと思いますが、ご指摘いただければ幸いです。あと、開発者の人脈が欲しいので、もしよろしければ [Twitter](https://twitter.com/_CrMoi90) のフォローをしていただければフォロバさせていただきます。長いこと閲覧いただきありがとうございました。