FlaskからMicrosoft Graph APIでOneDrive for Businessにアクセスしてみる
Pythonを使ってAPI経由で、
OneDrive for Businessのファイルを扱いたく、実装方法を調べていたのですが。
難しくは無いものの、
いろいろと混乱してしまい時間がかかったので、メモを残しておこうと思います。
アクセスするサービス・利用するAPI
APIを考える前に、
マイクロソフトが提供しているサービスには、
以下のように「OneDrive for Business」と似たサービスがあります。
- OneDrive ... 家庭向け、個人のファイルのクラウドストレージ
- OneDrive for Business ... 法人向け、個人のファイルのクラウドストレージ
- SharePoint Online ... 法人向け、組織での情報共有用のクラウドストレージ
これらを別のものと認識しておかないと、情報を調べるときに混乱してしまいます。
特に「OneDrive」「OneDrive for Business」が全く別物であることには注意が必要です。
このエントリでは、次の「Microsoft Graph API」を利用します。
Microsoft Graph
https://docs.microsoft.com/ja-jp/graph/overview
これに対しても似たようなものとして、
「Office 365 API」「SharePoint API」などがあるので、混乱しないように注意します。
特に、GitHubでサンプルを探す時など
Azure Active Directoryへのアプリの登録
アプリケーションを実装する前に、
Azure Active Directoryに実装するアプリを登録しておきます。
登録はAzureポータルから実施します。
Microsoft Azure ポータル
https://portal.azure.com/
登録方法は、以下を参考にして下さい。
Microsoft ID プラットフォームにアプリケーションを登録する | Microsoft Docs
https://docs.microsoft.com/ja-jp/graph/auth-register-app-v2
注意点としては、
- リダイレクトURIに
http://localhost:5000/login/azure/authorized
の指定が必要 - APIのアクセス許可に
Microsoft Graph - File.Read
の指定が必要
Flaskでの実装
次に、Flaskアプリケーションの実装を行います。
Flaskの環境作り
まず、単純なFlaskのアプリを作成しておきます。
venvの環境を用意して、Flaskをインストールします。
python -m venv venv
. venv/bin/activate
pip install Flask
以下のように「main.py」を作成します。
from flask import Flask
app = Flask(__name__)
@app.route('/')
def home():
return 'hello'
以下のコマンドで実行、ブラウザから表示を確認します。
export FLASK_APP=main.py
export FLASK_ENV=development
flask run
OAuth2による認証
FlaskアプリにOAuth2による認証を追加します。
ここではFlask-Danceを利用します。
Flask-Dance | GitHub
https://github.com/singingwolfboy/flask-dance
Flask-Danceをインストールします。
pip install Flask-Dance
アプリの登録で取得した、ClientKey, ClientSecretKeyを設定ファイルに記載します。
SECRET_KEY='※ここにSECRET KEY(アプリ固有の適当な文字列)※'
AUTHORITY='https://login.microsoftonline.com'
CLIENT_ID='※ここにアプリのClientKey※'
CLIENT_SECRET='※ここにアプリのClientSecretKey※'
main.pyを変更して、ログイン・ログアウト機能を実装します。
from flask import Flask, url_for, redirect, session
from flask_dance.contrib.azure import make_azure_blueprint, azure
import os
app = Flask(__name__)
app.config.from_pyfile('app.cfg')
conf = app.config
os.environ['OAUTHLIB_RELAX_TOKEN_SCOPE'] = '1'
blueprint = make_azure_blueprint(
client_id=conf["CLIENT_ID"],
client_secret=conf["CLIENT_SECRET"],
scope=['openid'],
redirect_url='{0}/'.format('http://localhost:5000/')
)
app.register_blueprint(blueprint, url_prefix="/login")
@app.route("/logon")
def logon():
return redirect(url_for("azure.login"))
@app.route("/logoff")
def logoff():
if 'azure_oauth_token' in session:
del session['azure_oauth_token']
if 'azure_oauth_state' in session:
del session['azure_oauth_state']
return redirect(url_for("home"))
@app.route('/')
def home():
if azure.authorized:
resp = azure.get("/v1.0/me")
return 'hello, {0}. / <a href="/logoff">logoff</a>'.format(resp.json()['displayName'])
return 'hello. / <a href="/logon">logon</a>'
OAUTHLIB_RELAX_TOKEN_SCOPEを指定しているのは、
Flask-Dance側、ActiveDirectoryのアプリ登録側のscopeが不一致でも、
認証を継続させるために指定しています。
変更出来たら、以下のコマンドで実行、ブラウザから表示を確認します。
ローカルの開発環境は、http(SSLでは無い)なので、
OAUTHLIB_INSECURE_TRANSPORTを指定して、実行させています。
export OAUTHLIB_INSECURE_TRANSPORT=1
flask run
OneDrive for Businessへのアクセス
後は、APIを呼び出して、OneDrive for Businessにアクセスするだけです。
例えば、home関数を以下のように書き換えると、
ルート階層のファイル一覧を表示することが出来ます。
@app.route('/')
def home():
if azure.authorized:
resp = azure.get("/v1.0/me/drive/root/children")
files = '<br>'.join([f['name'] for f in resp.json()['value']])
return '*file list*<br>{0}<br><br><a href="/logoff">logoff</a>'.format(files)
return 'hello. / <a href="/logon">logon</a>'
その他の、具体的な操作は、以下のドキュメントを見れば分かると思います。
Microsoft Graph でのファイルの作業 | Microsoft Graph | Microsoft Docs
https://docs.microsoft.com/ja-jp/graph/api/resources/onedrive?view=graph-rest-beta
tokenのリフレッシュ
OAuth2の認証で得たtokenは、有効期限があるので、
裏で定期的なバッチ処理を行う場合などは、tokenをリフレッシュする必要があります。
リフレッシュ用のrefresh tokenを取得したい場合は、
「Microsoft Azure ポータル」で
「APIのアクセス許可」にMicrosoft Graph - offline_access
の指定が必要になります。
tokenのリフレッシュ方法は、
Flask-Danceは、Requests-OAuthlibを利用しているので、以下が参考になります。
Refreshing tokens | Requests-OAuthlib
https://requests-oauthlib.readthedocs.io/en/latest/oauth2_workflow.html#refreshing-tokens
具体的に書くと、blueprint.session
の実体は、
Requests-OAuthlibのOAuth2Sessionなので、
以下のように呼び出すと、新しいtokenを取得する事が出来ます。
extra = {
'client_id': conf["CLIENT_ID"],
'client_secret': conf["CLIENT_SECRET"],
}
new_token = blueprint.session.refresh_token(blueprint.token_url, **extra)
実際にバッチ処理で扱う場合は、
token/refresh tokenをFlaskのセッション変数で無く、DB等に保持させる必要がありますが、
その対応は、Flask-Danceのドキュメントが参考になります。
Token Storages | Flask-Dance
https://flask-dance.readthedocs.io/en/latest/storages.html
バッチ処理・tokenのリフレッシュあたりは、やり出すと長いので、
以上のとおり、参考程度にとどめておきます。