PythonでBOXのAPIをOAuth認証してから使うサンプル

# はじめに

ファイル共有クラウドサービス「BOX」のAPIをPythonから利用するサンプルを紹介します。

APIの使い方自体は「BOX」のドキュメントはじめ、色々な人が紹介してくれているのですが
自分がやりたかったことに近い情報が無かったので記事にしました。

# やりたかったこと

* とりあえずBOXのAPIを使いたい
* macのターミナルから実行（本当はローカルのJupyterLabから実行）
* OAuthを使う
* OAuthのリダイレクト結果をWEBサーバーで受け取る
* Pythonで開発

https://github.com/box/box-python-sdk/blob/master/docs/usage/authentication.md
まずは、このSDKのサイトに載っている次のようなサンプルコードをサクッと動かしたかったのです。

```
client = Client(auth)
user = client.user().get()
print('User ID is {0}'.format(user.id))
```

動かすだけであれば手動でOAuthの認証を通すこともできますが、今回はOAuthを簡易な方法で実現してみました。


# 環境

```
Mac   10.14.6
Python  3.7.4
boxsdk  2.6.1 
```

# 準備

## BOXのSDKをインストール

環境に合わせてpipや他のツールでインストールして下さい。

```
pip install boxsdk
```

## BOXの管理画面でアプリを登録

https://developer.box.com

---
<img width="1059" alt="スクリーンショット 2019-12-22 20.22.49.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261675/397c3cdd-3b1e-2600-d48e-5388fea33942.png">

---
<img width="1114" alt="スクリーンショット 2019-12-22 20.19.46.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261675/43855ae0-a658-8352-9e40-186c5e1eb23d.png">

---
<img width="1027" alt="スクリーンショット 2019-12-22 20.17.20.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261675/c6fa0c1c-24ac-51ee-c905-0dbd648d757d.png">

ここに表示されるクライアントIDとクライアント機密コードをソースにコピペする。

リダイレクトURIには「 http://localhost:8080 」と入力する。

# サンプルコード

```sample.py
from boxsdk import OAuth2, Client
import webbrowser
import http.server
import socketserver
from urllib.parse import urlparse, parse_qs

# BOXの管理画面から取ってくる
CLIENT_ID = 'クライアントID'
CLIENT_SECRET = 'クライアント機密コード'

# BOXの管理画面に設定する
REDIRECT_URI = 'http://localhost:8080'

HOST = '127.0.0.1'
PORT = 8080

# BOXが発行する認証コードを入れる変数
global auth_code

auth_code = None

oauth = OAuth2(
    client_id = CLIENT_ID,
    client_secret = CLIENT_SECRET,
    store_tokens = None     # トークンの保管は今回は省略
)

# OAuth開始
auth_url, csrf_token = oauth.get_authorization_url(REDIRECT_URI)

# ブラウザ起動してBOXのIDとパスワードを入力する
# 入力するとREDIRECT_URIにリダイレクトされる
webbrowser.open(auth_url)

# REDIRECT_URIが叩かれた時の処理
class ServerHandler(http.server.SimpleHTTPRequestHandler):
    def do_GET(self):
        global auth_code
        self.send_response(200)
        self.send_header('Content-type', 'text/html')
        self.end_headers()
        self.wfile.write(b"<h1>Authenticated</h1>")
        parsed_path = urlparse(self.path)
        query = parse_qs(parsed_path.query)
        auth_code = query['code'][0]

with socketserver.TCPServer((HOST, PORT), ServerHandler) as server:
    print('http server start')
    # server.serve_forever()    # Ctrl+Cが押されるなどの割り込みがあるまで処理し続ける
    server.handle_request()     # １回リクエストを処理したら抜ける
    print('http server shutdown')

# auth_codeが取れたので、ここからAPIが使える
access_token, refresh_token = oauth.authenticate(auth_code)

client = Client(oauth)
me = client.user().get()
print('My user ID is {0}'.format(me.id))
```

# 実行

* 実行するとブラウザが立ち上がってBOXのIDパスワードの入力画面が表示される
* 正しいIDパスワードを入力すると、リダイレクトされて画面に「Authenticated」と表示される
* コンソールにBOX APIで取得したユーザーIDが表示される

# 注意点

* クライアントIDとクライアント機密コードはソースに直書きしている
* トークンの保管は今回は省略している
* セキュリティや耐久性等は考慮していない

# 最後に

JupyterLabで実行したかったので１ファイルにまとめて書きました。

Pythonで簡易のWebサーバーを起動するのは簡単ですね。今回は1リクエスト処理しただけでWebサーバーが終了するところが気に入っています。

逆に do_GETメソッド の中でグローバル変数に直接代入しているのは気に入らないところです。キレイな書き方ないかな？

多分おかしな動きはしていないと思いますが、まだまだPythonに詳しくないので変なところや改善点があったらご指摘頂けると幸いです。