Googleカレンダーのサーバーサイド用トークンをgoogle-authとoauthlibを使って取得する

2 min read読了の目安(約2100字

Googleカレンダーの情報をサーバーサイドで取得したい場合、Googleカレンダーのaccess_tokenとrefresh_tokenが必要になります。これを取得するには、ユーザーにGoogleアカウントでの認証を求める必要があります。

iOS/AndroidアプリでGoogleアカウントの認証を取る場合、アプリ側でauth_codeを取得し、それをサーバーサイドに送信し、サーバーサイド側でauth_codeを使ってGoogleカレンダーのaccess_tokenとrefresh_tokenと交換することになります。

公式のリファレンスはこちらこちらですが、サーバーサイド側のPythonコードで使われているoauth2clientはすでに非推奨になっています。 そのため、代わりに利用が推奨されている google-auth と oauthlib にサンプルコードを書き換える必要があります。

にも関わらず、アプリで取得した auth_code をサーバーサイドで access_token と refresh_token に交換するコードが公式ドキュメントどころかいくら探せど見つからず、めっちゃ苦労しました。 ので、ちゃんと動いたコードを載せます。

from google_auth_oauthlib.flow import Flow

flow = Flow.from_client_secrets_file(
           '/path/to/client_secret.json',
           scopes=[
               'openid',
               'https://www.googleapis.com/auth/userinfo.profile',
               'https://www.googleapis.com/auth/userinfo.email',
               'https://www.googleapis.com/auth/calendar'
           ]
       )

# クライアントで取得したauth_codeを渡す
credentials = flow.fetch_token(code=auth_code)


print(credentials.get('access_token'))
print(credentials.get('refresh_token'))
print(credentials.get('id_token'))

google_auth_oauthlibのFlowを使います。Googleコンソールから取得できるclient_secret.jsonを適当な場所において、そのパスを第一引数、認証のスコープを第二引数に渡します。このとき、第二引数のスコープはアプリでauth_codeを取得するときのスコープと一致させてください。

あとは、flow.fetch_token(code=auth_code)すれば、dictが帰ってくるのでそれぞれ、.get('access_token').get('refresh_token')でGoogleカレンダーにアクセスするためのトークンを取得できます。

id_tokenについては詳しい説明を割愛しますが、これを使ってemailやnameを取得します。

from google.oauth2 import id_token
from google.auth.transport.requests import Request

decoded_token = id_token.verify_oauth2_token(
   credentials.get('id_token'),
   Request(),
   'YOUR_GOOGLE_OAUTH2_CLIENT_ID'
)

if decoded_token.get('iss') not in ['accounts.google.com', 'https://accounts.google.com']:
   raise ValueError('Invalid oauth issuer.')

print(decoded_token.get('email'))
print(decoded_token.get('name'))

普通にありそうな状況なのに、日本語も英語も情報が少なくてかなり苦労しました。英語の公式ドキュメントも未だに非推奨のoauth2clientなので、oauth2clientが使われていることが多いのかもしれません。