LoginSignup
3
1

More than 1 year has passed since last update.

サービスアカウントファイルを使ってIDトークンを取得する

Last updated at Posted at 2021-09-08

はじめに

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なので考えてみれば当然なのだがわかりにくい……)。

おわりに

おわり。

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1