BLOGTIMES
2021/08/01

python-o365 を使って O365 アクセストークンを取得する

  office365  python  oauth2 
このエントリーをはてなブックマークに追加

Office 365(というか Exchange Online)に POP3 や IMAP、SMTP 接続しようとすると、いまのところは ID/PW による基本認証が使えますが、今年中にもこの機能が削除される予定*1になっているので、今後のことを考えると先進認証と呼ばれる SASL XOAUTH2 に対応させる必要があります。

Outlook や Thunderbird などのメーラーは設定を変更するだけですんなり対応できるので良いのですが、fetchmail などの非対話のコマンドラインツールなどを使おうとすると一気に面倒なことになります。基本的なやり方は以下にあるのですが、OAuth2 の設定するためには Azure AD などの仕組みについて理解する必要が出てきます。

パスワードの代わりになるものはアクセストークン

そして、今回はパスワードの代わりに利用されるアクセストークンの取得処理を以下のライブラリと Python を使って実装してみました。

モジュール自体はいつものごとくpip install O365でインストールできます。

アクセストークンを取得するスクリプト

最もシンプルに書いてみたところ、こんな感じになりました。

get_token.py

#!/usr/bin/env python # -*- coding: utf-8 -*- # BSD 2-clause from O365 import Account, MSGraphProtocol, FileSystemTokenBackend # TENANT_ID等はダミーなので適宜読み替えてください TENANT_ID = '99999999-9999-9999-99999999999999999' CLIENT_ID = '99999999-9999-9999-99999999999999999' CLIENT_SECRET = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' TOKEN_FILENAME='token.json' TOKEN_PATH='.' AUTH_FLOW='authorization' SCOPES = ['offline_access', 'https://outlook.office365.com/POP.AccessAsUser.All', 'https://outlook.office365.com/IMAP.AccessAsUser.All', 'https://outlook.office365.com/SMTP.AccessAsUser.All'] token_backend = FileSystemTokenBackend(token_path=TOKEN_PATH, token_filename=TOKEN_FILENAME) credentials=(CLIENT_ID, CLIENT_SECRET) account = Account(credentials=credentials, scopes=SCOPES, token_backend=token_backend, auth_flow_type=AUTH_FLOW, tenant_id=TENANT_ID) if not account.is_authenticated: account.authenticate() print('Authenticated!') else: account.connection.refresh_token() print('Refreshed!') with open('access_token', mode='w') as f: f.write(token_backend.get_token()['access_token'])

比較的簡単なモジュールですが、いくつか落とし穴があります。

まず、SCOPES の部分ですが、Azure AD でのアプリ登録の画面では https://graph.microsoft.com/POP.AccessAsUser.All となっていますが、Outlook にアクセスするためには https://outlook.office365.com/POP.AccessAsUser.All のように読み替えないと、アクセストークンが取得できても認証が通りません*2

また、python-o365 のバグなのか、以下のような感じで怒られるので、メッセージに従って無理やりパッチを当てました。

Unable to fetch auth token. Error: (invalid_client) AADSTS700025: Client is public so neither 'client_assertion' nor 'client_secret' should be presented. Trace ID: 99999999-9999-9999-99999999999999999 Correlation ID: 99999999-9999-9999-99999999999999999 Timestamp: 0000-00-00 00:00:00Z Something go wrong. Please try again.

O365.diff

--- a/O365/connection.py +++ b/O365/connection.py @@ -511,7 +511,8 @@ class Connection: token_url=self._oauth2_token_url, authorization_response=authorization_url, include_client_id=True, - client_secret=self.auth[1])) + )) elif self.auth_flow_type == 'public': self.token_backend.token = Token(self.session.fetch_token( token_url=self._oauth2_token_url, @@ -633,7 +634,8 @@ class Connection: self.session.refresh_token( self._oauth2_token_url, client_id=client_id, - client_secret=client_secret) + ) ) elif self.auth_flow_type == 'public': client_id = self.auth[0]

上手く認証ができると Authenticated!Refreshed! のメッセージが表示され、token.json ファイルにトークンの情報が、access_token ファイルにアクセストークンが書込まれるようになっています。アクセストークンの有効期限は1時間*3なので、cron 等でこのスクリプトを定期的に実行し続ける必要があります


トラックバックについて
Trackback URL:
お気軽にどうぞ。トラックバック前にポリシーをお読みください。[policy]
このエントリへのTrackbackにはこのURLが必要です→https://blog.cles.jp/item/12633
Trackbacks
このエントリにトラックバックはありません
Comments
愛のあるツッコミをお気軽にどうぞ。[policy]
古いエントリについてはコメント制御しているため、即時に反映されないことがあります。
コメントはありません
Comments Form

OpenID を使ってログインすることができます。

Identity URL: Yahoo! JAPAN IDでログイン