[GCP]サービスアカウントの ID トークンの発行

Cloud Run サービス、Cloud Run Functions 利用時に、その IAM 認証を突破するためにサービスアカウントの ID トークンが必要になる。

:::note
ID トークン
OIDC (OpenID Connect) で定義されている、ユーザーが認証されていることを証明するための JWT 形式のトークン。
:::



# gcloud コマンドで発行
[`gcloud auth print-identity-token`](https://qiita.com/WisteriaWave/items/12f92387ec58bae670e4#gcloud-auth-print-identity-token)
を使う。

例. 
```
gcloud auth login

# 自身の Google アカウントの ID トークンを発行
gcloud auth print-identity-token audiences="<オーディエンス>"

# サービスアカウントになりすましての ID トークンを発行
gcloud auth print-identity-token --impersonate-service-account="<サービスアカウント>" audiences="<オーディエンス>"
```

なお、`gcloud auth print-identity-token` を利用して、サービスアカウントになりすましてサービスアカウントの ID トークンを発行する場合は、なりすまし先サービスアカウントに対する `roles/iam.ServiceAccountOpenIdTokenCreator` ロールをなりすまし元の Google アカウントに付与しておく必要あり。

`roles/iam.ServiceAccountTokenCreator`ロールは不要。
なお、ロールに含まれるパーミッション的には、
`roles/iam.ServiceAccountTokenCreator` ⊇ `roles/iam.ServiceAccountOpenIdTokenCreator`の関係である。


# REST API で発行
[projects.serviceAccounts.generateIdToken API](https://cloud.google.com/iam/docs/reference/credentials/rest/v1/projects.serviceAccounts/generateIdToken) を使う。

## エンドポイント
```
POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/<認証情報を要求するサービスアカウント>:generateIdToken
```

## リクエストヘッダ
`Authorization: Bearer` には 「ID トークンを発行したいサービスアカウントに対する`roles/iam.ServiceAccountOpenIdTokenCreator`ロールが付与されているプリンシパル」の OAuth2.0 アクセストークンをセット。
ADC に含まれる source_credencials の情報を使う方法もあるみたいだが、Google 公式のドキュメントを明後日も伺いしれなかった。
・・・とはいえ、`Authorization: Bearer`にアクセストークンをセットする方法も、とあるサンプル実行からわかっただけで、API 仕様として書かれているドキュメントはない(はず)。

## リクエストボディ
```
{
  "delegates": [
    <必要あれば>
  ],
  "audience": <オーディエンス>,
  "includeEmail": true
}
```

**`delegates[]:`**
デリゲーションチェーンにおけるサービスアカウントのシーケンス。
各サービスアカウントは、チェーンの次のサービスアカウントの `roles/iam.serviceAccountTokenCreator` ロールを付与されている必要あり。
チェーン内の最後のサービスアカウントは、リクエストのnameフィールドで指定されたサービスアカウントのroles/iam.serviceAccountOpenIdTokenCreatorロールを付与されなければならない。
デリゲートは、以下の形式でなければならない: `projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}`
ワイルドカード文字の`-`は必須。

**`audience:`**
必須。トークンの利用者。このトークンがアクセスを許可する API やアカウントなど。

**includeEmail:**
トークンにサービスアカウントのEメールを含めるか。
true に設定すると、トークンに `email` と `email_verified` クレームが含まれる

## 実行例. 
curl で叩くと以下のような形
```
curl -X POST \
     -H "Authorization: Bearer <サービスアカウントに対する >" \
     -H "Content-Type: application/json; charset=utf-8" \
     -d <リクエストボディ> \
     "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/<サービスアカウント>:generateIdToken"
```

# 各言語の認証ライブラリで発行
Go だと [idtoken](https://pkg.go.dev/google.golang.org/api/idtoken)。
ADC を使っていい感じに ID トークンを発行してくれる。

ローカルで使う場合は、`gcloud auth application-default login` で ADC をセットした状態で利用する。

`gcloud auth application-default login --impersonate-service-account="<サービスアカウント>"` で ADC が Google アカウントからサービスアカウントになりすます形になっている場合は、idtoken では以下のような処理になる模様。

1. Google アカウントの source_credentials から サービスアカウントのアクセストークンを発行 
    - 対象のサービスアカウントに対する `roles/iam.ServiceAccountTokenCreator` ロールが Google アカウントに付与されている必要あり
2. サービスアカウントのアクセストークンからサービスアカウントの ID トークンを発行
    - 対象のサービスアカウントに対する `roles/iam.ServiceAccountOpenIdTokenCreator` ロールが サービスアカウント自身に付与されている必要あり

ちなみに、他の言語の ID トークン発行処理だと Google アカウントの source_credentials から直接サービスアカウントの ID トークンを発行(対象のサービスアカウントに対する `roles/iam.ServiceAccountOpenIdTokenCreator` ロールが Google アカウントに付与されている必要あり)という形になっているらしい([Issue](https://github.com/googleapis/google-api-go-client/issues/2301)。ややこしい・・・


# 参考

https://cloud.google.com/docs/authentication/get-id-token?hl=ja

https://cloud.google.com/iam/docs/create-short-lived-credentials-direct?hl=ja#rest

https://cloud.google.com/run/docs/authenticating/service-to-service?hl=ja#use_the_authentication_libraries