django-allauthを使ってsteamにソーシャルログインするまで

## 概要

この記事は主にDjangoの基礎的事項を習得した人たちがdjango-allauthによるソーシャルログイン機能の実装を行いたい場合に役立てるように執筆しております。よくあるOAuth2.0の認証に関するallauthのドキュメントはいくつか存在していますが、OpenID(OAuthのちょっと強い版)を使用したsteamに対する認証方法に関する記事は少なかったため、ここに備忘録として残しておきます。具体的には、認証、steamのユーザー情報の取得、IDから所有タイトルの取得までを記事として残します。対象とするバージョンはDjango2.1を用いるものとします。

## django-allauthとは
django-allauthとはOAuthなどの「権限の認可(authorization)を行うための処理を行うライブラリ」です。より直感的な解釈としてはQiitaにもある「Google IDでログインを手っ取り早く実装するためのライブラリ」と言えます。一般にこれらの認証はOAuthという規格で行われるため、一つ実装すれば他も大体同じなわけですがコールバックアドレスなどいくつかのパラメータの差異が存在します、またsteamのようにOAuthから派生したOpenIDなどの規格を使っているウェブサイトも存在しています。django-allauthではそれらのソーシャルログインを行うウェブサービスごとの差異を吸収し容易に実装することを可能とします。

django-allauthが対応している認証先のウェブサービスの中で主要どころ（筆者の主観）は次のサイトです。

* Amazon (OAuth2)
* Azure (OAuth2)
* Baidu (OAuth2)
* Battle.net (OAuth2)
* Dropbox (OAuth, OAuth2)
* Evernote (OAuth)
* Facebook (both OAuth2 and JS SDK)
* Github (OAuth2)
* GitLab (OAuth2)
* Google (OAuth2)
* Instagram (OAuth2)
* Line (OAuth2)
* Microsoft (Graph) (OAuth2)
* Paypal (OAuth2)
* Reddit (OAuth2)
* Slack (OAuth2)
* Steam (OpenID)
* Twitch (OAuth2)
* Twitter (OAuth)
* Yahoo (OAuth2)

このほかのウェブサービスによるソーシャルログインも実装されていますが全て載せると長いので省略しています。

## 実装(共通)

この章ではsteamやそれ以外でも共通の実装部分を記します。

### インストール

インストールは通常のライブラリのインストールと同様です。本記事では次の二行を`requirements.txt`に書き込んでおく方法を示します。(もちろん`pip install django-allauth`でも可能)

````requirements.txt
django >= 2.1.6, < 2.2
django-allauth
````
執筆時現在のDjango2.2だとpymysqlの調子がよろしくないのでDjango2.1を使用しておりますがほかのDjango1.11以上のバージョンであれば同様のインストールが可能です。

### セッティング(Django)

ひとまず、Djangoのインストールが終わり、プロジェクトも作成したものとします。プロジェクトの作成に関してはここには記述していませんので別の記事を参考にしてください。(Visual Studio 2019だと簡単でいいですよ)

次に`setting.py`に必要な設定を書き込んでいきます。まずINSTALLED_APPSに次の情報を書き足していきます。地味に忘れやすいのが`django.contrib.sites`がデフォルトで追加されていない点です。公式ドキュメントでも割と大きく書いていたのでおそらくみんな間違えたんだと思います・・・。

````python:setting.py
INSTALLED_APPS = (
    ...
    # The following apps are required:
    'django.contrib.auth',
    'django.contrib.messages',

    'django.contrib.sites', # add

    'allauth', # add
    'allauth.account', # add
    'allauth.socialaccount', # add
    'allauth.socialaccount.providers.steam', # add

)
````

一番下にsteam用のプロバイダーを追加していますが、もし追加したいのがtwitterなら`'allauth.socialaccount.providers.twitter'`、Facebookなら'allauth.socialaccount.providers.facebook'を追加してください。ここではsteam一つのみですが複数の認証を追加することができます。

次に同じく`setting.py`に次の記述をまとめて追加してください。

````python:setting.py
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',
)
SITE_ID = 1
````

`url.py`に次の記述を追加して今回追加したdjango-allauthのログインページなどをまとめて有効化します。

````python:url.py
urlpatterns = [
    url(r'^accounts/', include('allauth.urls')), # add
]
````

ここまで終わったら **一度マイグレーション** をかけてください。

### ソーシャルログイン情報の追加

次にadminページにログインしてソーシャルログインに必要なパラメータをデータベースに登録します。スーパーユーザーが必要なため事前に作成してかadminページ( http://localhost:8000/admin/ )にアクセスしてください。

SOCIAL ACCOUNTS(外部アカウント) > social applicationにフィールドの追加を行いパラメータを入力します。今回のsteamであれば次のようにパラメータを設定します

* プロパイダー : Steam
* Name : Steam
* Client ID(API key) : 必要ないが必須フィールドであるためNoneなど適当な文字列OAuth系であれば必須情報だがSteamにはない
* Secret Key : [Steam WEB API key](https://steamcommunity.com/dev/apikey)（リンク先のサイトから取得可能）
* key : 不要
* Sites : なんでもok、デフォルトでhttps.examples.comが入っているためそれを選択。


![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/241757/059ed842-f428-e698-7a5c-dcbe37b35819.png)

ただしSitesには一つ注意事項がある。サイト > サイト(sites)に追加しているサイトが選択肢として追加されているため任意のサイトを追加することが可能。ただしここに新たなサイトを追加した場合`setting.py`内の`SITE_ID=1`を**登録したサイトに自動的にナンバリングされるIDに変更する**必要がある。

TwitterやFacebookであればそれに合わせて`setting.py`にプロパイダーを追加し、API KeyやSecret Keyを登録すればよい。

## 実装(steam)

これまでで一通りのソーシャルログインに必要な設定は終了した。
ここからはログイン後、steamのID情報を取得して、そこからさらに所有しているゲームに関する情報を取得する。ソーシャルログインに関してはここまでの設定が完了していれば http://localhost:8000/accounts/steam/login にアクセスすることで、そこから自動的にsteamに接続され、そこでアカウントにログインすれば自分のサイトにリダイレクトされ、Steam IDをはじめとするユーザー情報が取得される。

### SteamIDの取得

steam APIにはGetOwnedGamesという所持しているゲームをすべて取得するAPIが存在している。しかしこのAPIはSteamIDとAPI Keyが必要となる。ここで言うSteamIDとは普段steamにログインするときに使用するIDではなく、自動的に各ユーザに割り振られる数値列のことを指す。

したがってまずログインしているユーザーがログインするときに使用したアカウントのSteamIDを取得する必要がある。これはSocialAccountモデルに自動的に保存されているため、それを取得すればよい。例えば`view.py`のいずれかのviewでsteamIDを取得してcontextデータに格納したいのであれば次のようなviewを作ればよい。

````python:view.py
from allauth.socialaccount.models import SocialAccount

class ExampleView(TemplateView):
    template_name = "app/index.html"
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        
        # login check
        if self.request.user.is_authenticated:
            for account in SocialAccount.objects.filter(user=self.request.user):
                provider_account = account.get_provider_account()
            
        context["title"] = "Home"
        context["SteamID"]=provider_account
        return context
````

この時変数`provider_account`にはsteamIDが含まれる。またtwitterなどでソーシャルログインしていれば、ソーシャルログイン先のIDがこの変数には格納されている。それをcontextに格納したため、テンプレートの変数としてIDを利用できる。

例えばテンプレート内で次にようにすれば、ログインしているユーザには、自分のSteamIDが表示されることになる。

````html:index.html
<h1>{{SteamID}}</h1>
````

### Steamで所持しているゲームの取得

最後にSteamで所持しているゲームを取得する。方法としては先ほど述べたようにGetOwnedGamesというAPIを使う。このAPIには二つのパラメータを必要としている点は先ほど述べたとおりであるが。OpenIDなどのソーシャル連携をしていようがしていまいが、プライバシー設定を公開にしているアカウントからしか情報を取得できないという特徴がある。これは他人のゲームを見れるというメリットと、自信のウェブサービスにログインしているユーザのゲームを見れないかもしれないというデメリットがあることを意味している。ここはOAuthのソーシャルログインのようにログインしたらAPI叩き放題とは少し違うところである。

所持しているゲームを公開にする設定の具体的な方法は次のサイトを参照していただきたい。

[Steamでプレイ中のゲームや所持本数を非公開にする方法 | maruhoi1's blog](https://maruhoi.com/steam/steam-privacy-settings-2018/)

プライバシー設定に関しては取得できなかった時に、設定してくださいとお願いするページを表示するくらいしかできないのでこの程度にしておく。

先ほどまでにログインとSteamIDの取得が完了していると仮定し、ここからAPIを叩いて情報を取得するコードを追加します。今回は主にdjango-allauthのソースコードを参考にする形で開発を行いました。

例えば先ほどの`ExampleViewクラス`に次のようなメソッドを追加・実行することで所持しているゲームのデータ（プレイ時間も・・・）を取得することができます。

````python:view.py
    def get_steam_account_owned_games(self,api_key, steam_id):
        api_base = "http://api.steampowered.com/"
        method = "IPlayerService/GetOwnedGames/v0001/"
        params = {"key": api_key, "steamid": steam_id,"format":"json","include_appinfo":"1"}
        resp = requests.get(api_base + method, params)
        data=resp.json()
        return data

````

このメソッドはSteam APIキーと取得したSteamID(先ほどはprovider_accountに格納)を引数とし、GetOwnedGames APIを叩いてその結果をディクショナリとしてリターンします。このほかの[Steamworks Web API Reference (Steamworks ドキュメント)](https://partner.steamgames.com/doc/webapi?language=english)に存在するメソッドも`api_base`、`method`、`params`を変更することで実行することができます。ただし同じsteamのAPIでも、httpsのAPIとhttpのAPIがあるので`api_base`は変更いらないなと思っても注意が必要です・・・(私は事故った)。

この後は取得したデータをもとに、統計処理するなりビジュアライゼーションするなりオリジナルの処理を記述することができます。~~私は積みゲーが５００本くらいありました。~~


## 最後に

今後、ここまで行ったソースコードをVisual Studio 2019のプロジェクトとしてGitHubにて公開する予定です。誤字脱字、よりスマートな実装などがありましたらコメントいただければ幸いです。それではよいDjangoライフに役立てていただければ幸いです。