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のリフレッシュあたりは、やり出すと長いので、
以上のとおり、参考程度にとどめておきます。