はじめに
Google Cloud Platformの機能を使う場合、たいていは GOOGLE_APPLICATION_CREDENTIALS
にサービスアカウントファイルのパスが設定されていることを前提とするので認証について深く考えず使えることが多い。
が、GOOGLE_APPLICATION_CREDENTIALS
を使わず、サービスアカウントファイルをプログラム内で直接指定してREST APIを叩きたい時もある。
この場合、REST APIのリクエストのヘッダーには Authorization: Bearer <IDトークン>
なるおまじないを埋め込まなくてはいけない。
このIDトークンの取得の仕方をよく忘れるのでここでメモっておく。
なお、あらかじめサービスアカウントには該当のCloud Functionsに対して Cloud Functions 起動元 (roles/cloudfunctions.invoker)
のIAMが付与されていることを前提とする。
サービスアカウントファイルを使ってIDトークンを取得する
特にひねりもなく回答をのっける。
実行環境
- Python 3.9
必要パッケージのインストール
pip install google-auth
サービスアカウントファイルを使ってIDトークンを取得する関数
from typing import Any, Dict, Union
from google.auth.transport.requests import Request
from google.oauth2.service_account import IDTokenCredentials
def get_id_token_from_service_account(
service_account: Union[str, Dict[str, Any]], target_audience: str
) -> str:
if type(service_account) is str:
credentials = IDTokenCredentials.from_service_account_file(
service_account,
target_audience=target_audience,
)
else:
credentials = IDTokenCredentials.from_service_account_info(
service_account,
target_audience=target_audience,
)
credentials.refresh(Request())
token = credentials.token
return token
使い方の例
以下の例では、最終的にhttpトリガーのCloud Functions https://my_region-my-project.cloudfunctions.net/hello_world
を実行したかったものとする。
hello_wolrd
関数は認証が必要な関数であるためIDトークンが必要、というシチュエーションである。
import json
import requests
url = "https://my_region-my-project.cloudfunctions.net/hello_world"
# サービスアカウントファイルのパスを与えてIDトークンを取得する。
id_token = get_id_token_from_service_account("./my-project-xxxxxxxxxxxx.json", url)
# 何らかの理由で既に service_account_obj = json.load(open("./my-project-xxxxxxxxxxxx.json")) のようにして
# サービスアカウントファイルを自前で読み込んでJSONパーズ済みの場合は
# id_token = get_id_token_from_service_account(service_account_obj, url)
# のようにパース結果を直接指定してもいい。
# Cloud Functionsを実行する。
# ヘッダーの設定
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {id_token}",
}
# リクエストボディの設定
data_encoded = json.dumps({...}).encode()
response = requests.post(
url,
headers=headers,
data=data_encoded,
)
print(response.text)
補足1
Cloud Functions以外でもGCPのREST APIなら get_id_token_from_service_account
の第2引数にAPIのサービスエンドポイントを指定すればいいと思う(未確認)。
-> 他のGCP REST APIはIDトークンではなくアクセストークンを指定する。
ソースコードも変わってくる。(後日追記予定)
2022/04/03 補足2
最近 第2世代Cloud Functions がサポートされた(執筆時点ではpre-GAだが)。
第2世代でも上述のソースコードで呼び出すことができるようだ。
ただし第2世代の場合は 該当のCloud Functionsに対して Cloud Functions 起動元 (roles/cloudfunctions.invoker)
のIAMを付与するだけでなく、そのCloud Functionsと対応するCloud Runに対して Cloud Run 起動元(roles/run.invoker)
も付与しなければならない ことに注意する。
(第2世代のCloud Functionsの中身はCloud Runなので考えてみれば当然なのだがわかりにくい……)。
おわりに
おわり。