PythonでOAuth2認証ライブラリと言ったら、多分outhlibがよく使わていると思われます。
私も何度もお世話になってましたが、最近仕事で自動化をしていたときにoauthlib(request-oauthlib)ではうまくいかないAPIがありまして、別のOAuth認証ライブラリのauthlibを使ってみることにしました。
※当人はOAuth2認証の知見は素人レベルなので、間違いがあったら教えてもらえたら嬉しいです🙏
TL;DR
コードを見た方が早い人は、こちらのリポジトリがサンプルコードです。
https://github.com/hrsano645/python-authlib-example
うまくいかないAPIと出会う
本業で利用しているMFクラウド請求書のAPIが発端です。自動化をする上でAPIは非常に便利に利用できます。
そのAPIのバージョンアップをすることになったようで、新しい方法でAPIの認証を用意することに。
マネーフォワード クラウド請求書API スタートアップガイド(v3ご利用者さま向け) | マネーフォワード クラウド請求書サポート
v3になったところで、クライアントの認証方式がclient_secret_basic
とclient_secret_post
が選べるようになったようです。
ここでいうこの2つの認証方式はOAuth2クライアント認証のことをさしているようです。
- OAuth 2.0 クライアント認証 - Qiita
- https://qiita.com/TakahikoKawasaki/items/63ed4a9d8d6e5109e401#12-%E3%82%AF%E3%83%A9%E3%82%A4%E3%82%A2%E3%83%B3%E3%83%88%E3%82%BF%E3%82%A4%E3%83%97
ざっくりいうと、client_seret_post
はリクエストパラメーター(URIに入れるパラメーター)でクライアントIDとクライアントシークレットを送っています。
https://qiita.com/TakahikoKawasaki/items/63ed4a9d8d6e5109e401#13-client_secret_post
client_secret_basic
はクライアントIDとクライアントシークレットをbase64でエンコードした上でhttpヘッダーへ追加する、いわゆるHTTP Basic認証を使っている方法です。
https://qiita.com/TakahikoKawasaki/items/63ed4a9d8d6e5109e401#14-client_secret_basic
今回のAPIではこの2方式が選べるのですが、Pythonのoauthlib(2023-08-27現在のstable版)ではこのクライアントの種類をうまく識別できない模様です。client_secret_post
がAPIの前バージョンの方式だったようなので設定してコードを動かしたらエラーで動作せず。
Traceback (most recent call last):
[oauthlibのOAuth2Client.fetch_tokenを動かした時]
File "/Users/hiroshi/Documents/workspace/bproj1/.venv/lib/python3.11/site-packages/requests_oauthlib/OAuth2_session.py", line 366, in fetch_token
self._client.parse_request_body_response(r.text, scope=self.scope)
File "/Users/hiroshi/Documents/workspace/bproj1/.venv/lib/python3.11/site-packages/oauthlib/OAuth2/rfc6749/clients/base.py", line 427, in parse_request_body_response
self.token = parse_token_response(body, scope=scope)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/hiroshi/Documents/workspace/bproj1/.venv/lib/python3.11/site-packages/oauthlib/OAuth2/rfc6749/parameters.py", line 441, in parse_token_response
validate_token_parameters(params)
File "/Users/hiroshi/Documents/workspace/bproj1/.venv/lib/python3.11/site-packages/oauthlib/OAuth2/rfc6749/parameters.py", line 448, in validate_token_parameters
raise_from_error(params.get('error'), params)
File "/Users/hiroshi/Documents/workspace/bproj1/.venv/lib/python3.11/site-packages/oauthlib/OAuth2/rfc6749/errors.py", line 399, in raise_from_error
raise cls(**kwargs)
oauthlib.OAuth2.rfc6749.errors.InvalidClientError: (invalid_client) [A157304] The client authentication method is 'client_secret_post' but the request does not include a client secret.
どうやらサーバー側からクライアント認証の方式がそのとおりになっていないとして弾かれているようです。
この認証のコードはこちらを参考にしています。
https://requests-oauthlib.readthedocs.io/en/latest/examples/google.html
※oauthlibのクライアントを作るときに適切なパラメーターを入れたら動くかもしれません。検証不足ではあります。MFクラウド請求書v3のガイドを見た限りだと、CURLを使ったガイドでは 似たような方法でした (修正:2023-08-27)BASIC認証のようでした。
※oauthlibはclient_secret_basic
を扱うっぽいですが、(追記:2023-08-27)実際に認証とトークン取得まではうまくいくものの、リフレッシュトークンでトークン更新を行おうとすると上記と似たようなエラーで弾かれてしまってました。
Authlibを使う
ここで動かないとなると、本業が何も進まなくなって影響が大きく困ってましたが、PythonのOAuth認証向けのライブラリはいくつか種類があるようで、別のものを使ってみようと調べてみるといくつか上がっています。
ここにあるAuthlibが利用できそうです。ドキュメント上にクライアント認証のタイプについての言及もあるので、サポートは良さそうです。(AuthlibはOSS版と有償版があります)
クライアント
https://docs.authlib.org/en/latest/client/oauth2.html#client-authentication
requestsとの統合もされていて、 requests-oauthlibの代替できるようなクラスの作りにもなっています(が渡すパラメーター名や操作方法に違いがあるので注意が必要)
- https://docs.authlib.org/en/latest/client/requests.html
- https://docs.authlib.org/en/latest/client/oauth2.html
あとはサンプルコードにて
Authlibで実際にサンプル的なコードを作ってみました。実際に利用しているコードとほとんど同じもので、
https://github.com/hrsano645/python-authlib-example
MFクラウド請求書APIでは、デフォルトはclient_secret_basic
なので、これに対応したものにしています。
Authlibのドキュメントでは、リフレッシュトークンの扱いとトークン自体の保存についてはあまり言及がなかったので苦労しましたが、トークンを適切に保存して、再利用するように作れば問題なく扱えるようです。
まとめ
PythonのOAuth認証ライブラリのAuthlibを使ってみた話でした。日本語圏ではあまり記事になっていなかったので、参考になりましたらと思います。