[Django]  social-auth-app-djangoを用いてGoogle OAuth2ログイン認証をする

## はじめに
Googleアカウントを使ったログイン機能がほしかったので作りました。
[social-auth-app-django](https://github.com/python-social-auth/social-app-django)を使うと簡単に作れるみたいなので、ありがたく使わせていただきます！

>今回はGoogleアカウントを使ったログイン機能を作ることに焦点を当てているため、RESTなどはあまり意識していません。

- [Document](https://python-social-auth.readthedocs.io/en/latest/index.html)
- [DocumentのDjangoのページ](https://python-social-auth.readthedocs.io/en/latest/configuration/django.html)

## 前知識
### social-auth-app-djangoとは
- DjangoでOAuth認証を行う時に使うサードパーティ製のパッケージ
- Google, Twitter, Facebook などのプロバイダーのアカウントを使って認証処理ができる

## 環境
- python 3.7.4
- django 3.0.3

## 作業開始
### social-auth-app-djangoをインストール
```terminal
$ pip install social-auth-app-django
```

### アプリケーションの登録
**settings.py**の`INSTALLED_APP`に`social_django`を追加

```python:setting.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'social_django',# これを追加した
]
```
>デフォルトでDjango ORMが使用されるため、もしMongo ORMを使いたい時は別の処理が必要です。
Mongo ORM使いたい方へ　[参考](https://python-social-auth.readthedocs.io/en/latest/configuration/django.html#register-the-application)

### DB
social_djangoを`INSTALLED_APPS`に登録したら、`migrate`を行い、DBを同期させ、必要なモデルを構築します。

```terminal
$ ./manage.py migrate
```

### 認証バックエンドの登録
次に、認証バックエンドを登録していきます。
**settings.py**に`AUTHENTICATION_BACKENDS`を定義して、使いたい認証バックエンドを追加します。

```python:settings.py
AUTHENTICATION_BACKENDS = (
    'social_core.backends.open_id.OpenIdAuth',
    'social_core.backends.google.GoogleOAuth2',
    'social_core.backends.google.GoogleOAuth',
    'social_core.backends.twitter.TwitterOAuth',
    'social_core.backends.yahoo.YahooOpenId',
    'django.contrib.auth.backends.ModelBackend',
)
```
最後の行にある`django.contrib.auth.backends.ModelBackend`を忘れると`django.contrib.auth`を使った認証がうまくできなくなります。ログインできない時はここが抜けてないかチェックしてみてください。

>このパートは、認証に使いたいプロバイダーに合わせて認証バックエンドを登録する必要があります。
[サポートされているプロバイダー](https://python-social-auth.readthedocs.io/en/latest/backends/index.html)

### Context_Processorsを登録
**settings.py**の`TEMPLATES`にcontext_processorを追加します。

```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', # 追加
                'social_django.context_processors.login_redirect', # 追加
            ],
        },
    },
]
```

backends context processer の中には以下の３つの情報が辞書型として含まれています。

- **associated** : 現在ログインしているユーザーに関連する`UserSocialAuth`のインスタンスのリスト(ユーザーがいない場合は空)
- **not_associated** : 現在ログインしているユーザーに関連付けされていない利用可能なバックエンド名のリスト
- **backends** : 利用可能なすべてのバックエンド名のリスト

### URLの設定
トップページ（誰でも見れる） → ログインページ → ホームページ（ログインしないと見れない）
の流れでアクセスできるようなURLを作成していきます

```python
from django.contrib import admin
from django.urls import path
from django.urls.conf import include
from django.contrib.auth.views import LoginView, LogoutView
from . import views

urlpatterns = [
    path('', views.toppage, name='toppage'),
    path('admin/', admin.site.urls),
    path('auth/', include('social_django.urls', namespace='social')), # これが重要
    path('login/', LoginView.as_view(), name='login'),
    path('logout/', LogoutView.as_view(), name='logout'),
    path('home/', views.home, name='home'),
]
```
ログインしたらログアウトできないとお辛いので、ログアウトもできるようにしときましょう。
最後に、`LOGIN_URL`と`LOGIN_REDIRECT_URL`を指定します。

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

### クライアントIDとシークレットキーを取得
[Google API Consolse](https://console.developers.google.com/projectselector2/apis/library?supportedpurview=project)に移動し、新しいプロジェクトを作成し、認証情報を作成します。
その際に

- 承認済みの JavaScript 生成元
- 承認済みのリダイレクト URI

それぞれ聞かれるので

承認済みの JavaScript 生成元を

```
http://localhost:8000
```

認証済みリダイレクトURIを

```
http://localhost:8000/auth/complete/google-oauth2/
http://127.0.0.1:8000/auth/complete/google-oauth2/
```
と設定して、保存してください。
今回はローカルで動かしているので、これで問題ないのですが
もし、本番環境で使う場合はドメインの部分を変更してください。

クライアントIDとシークレットキーを取得できたら、**settings.py**
にそれぞれ記載します。

```python
SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = 'hogehoge' # クライアントID
SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET 'hugahuga' # シークレットキー
```

ベタガキしたくない人は環境変数に持たせるといいかもしれません


### views.pyの編集
```python
from django.contrib.auth.decorators import login_required
from django.shortcuts import render


def toppage(request):
    return render(request, 'toppage.html')


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

```
htmlをレンダリングするシンプルな関数ですね

### Templateの作成
templates内にhtmlファイルを作成していきます
この辺の詳しい説明は省きます

```html:base.html
<!DOCTYPE html>
<html lang="en">
  <head>
      <meta charset="UTF-8">
  </head>
  <body>
      {% block content %}
      {% endblock %}
  </body>
</html>
```

```html:toppage.html
{% extends 'base.html' %}

{% block content %}
  <h1>Hello World!!</h1>
  <a href="{% url 'login' %}">ログイン</a>
{% endblock %}
```

```html:home.html
{% extends 'base.html' %}

{% block content %}
{% if user.is_authenticated %}
  <h1>ログイン成功！！</h1>
  <h2>{{ user.username }}がログイン中</h2>
  <p>is_authenticated : {{ user.is_authenticated }}</p>
  <p>user_name : {{ user.username }}</p>
  <p>backends: {{ backends }}</p>
{% endif %}
<a href="{% url 'logout' %}">ログアウト</a>

{% endblock %}
```

`home.html`時点で認証は成功していますが、念のため`is_authenticated`で認証済みか確認しています


次に、ログインページとログアウトページを作成しますが、`LoginView`と`LogoutView`の仕様に沿って、**templates**に**registration**ディレクトリを作成して、その中に　**login.html** と **logged_out.html**ファイルを作成します。


以下、**templates**の構造
>templates/
 ┣ registration/
 ┃ ┣ login.html
 ┃ ┗ logged_out.html
 ┣ base.html
 ┣ home.html
 ┗ toppage.html

```html:login.html
{% extends 'base.html' %}

{% block content %}
  <h2>ログインに使用するサービスを選択してください
    <ul>
        <li><a href="{% url 'social:begin' 'google-oauth2' %}">Googleアカウントでログイン</a></li>
    </ul>
{% endblock %}
```


```html
<li><a href="{% url 'social:begin' 'google-oauth2' %}">Googleアカウントでログイン</a></li>
```
この部分が認証画面に飛ばしてくれます。

```html:logged_out.html
{% extends 'base.html' %}

{% block content %}
  <h2>ログアウトしました</h2>
  <ul>
      <li><a href="{% url 'landing' %}">ランディングページへ</a></li>
      <li><a href="{% url 'login' %}">ログインページへ</a></li>
  </ul>
{% endblock %}
```
これで準備ができましたので、実際に動かしてみましょう

### 動作確認
トップページ
![スクリーンショット 2021-02-21 13.48.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/667943/6470f24f-fdfd-181a-3d08-661b1b813922.png)

ログインページ
![スクリーンショット 2021-02-21 13.48.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/667943/f645309f-1d45-b371-70ef-1c54adf772dc.png)

ホームページ
![IMG_0216.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/667943/d97f78d2-2fce-ff3c-91c6-f40905b72560.png)

ログアウトページ
![スクリーンショット 2021-02-21 13.50.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/667943/5d1e2421-e1ec-283c-acf1-20c6d5fbfa9c.png)

意図した挙動してますね！

##終わりに
**social-auth-app-django**を使うとこんなにも簡単にログイン機能が作れるんですね！
ちょっとびっくりしています。
この記事が誰かのためになれたら幸いです！

ここに書いてある事が絶対正しい！！というわけではないので、間違いなどのご指摘がございましたらコメントお願いします！🙇‍♂️