AppEngineでIAPアクセス制限したWebAPIにリクエストを投げる方法

## Cloud Identity-Aware Proxy の簡単な説明

Cloud Identity-Aware Proxy （Cloud IAP） は、Google Cloud Platform に含まれるセキュリティプロダクトの１つです。 Cloud IAP を使用することで、App Engine にデプロイしたサービスに、簡単なアカウント認証を設けることができます。

通常はアプリケーションごとに独自のログイン認証を設けることが多いと思いますが、 Cloud IAP による認証は、アプリ独自の認証と違い、インスタンスが立ち上がる前に拒否できるので、不要なインスタンスが立ち上がるのを防ぐことができます。

静的IPがない状況で手っ取り早くアクセス制限したい場合や、課金インスタンスの増加を抑制したい場合は、便利な機能です。

詳しくはこちらへ
https://cloud.google.com/iap/docs/concepts-overview

## 何が問題か？
GUIベースのWebアプリケーションであれば、特に問題はありません。手順に従って、アクセス制限を設定すれば、URLにアクセスしたときにGoolgeの認証画面が表示され、許可されたアカウントでログインすれば、アプリケーションを利用することができます。

問題は、WebAPIの場合です。

WebAPIの場合でも、ブラウザからのアクセスであれば、WebAPIも問題なくアクセスできます。ですが、ブラウザを経由しないアクセスでは、認証画面を表示できないので認証が通りません。

さぁ、困った。

## サービスアカウント認証

そんなときに役立つのが、サービスアカウント認証です。サービスアカウント認証の手順は下記のドキュメントに書いてあります。

https://cloud.google.com/iap/docs/authentication-howto#authenticating_from_a_service_account

引用すると、

> 1. Cloud IAP で保護されたプロジェクトのアクセスリストにサービス アカウントを追加します。
> 2. JWT ベースのアクセス トークンを生成します。これは、クライアント ID を必要とする target_audience 追加要求を使用します。クライアント ID を確認する手順は次のとおりです。
>
>     1. [Cloud IAP] ページに移動します。
>     2. アクセスするリソースを見つけて、[詳細] > [OAuth クライアントを編集] をクリックします。
>     3. 表示される [認証情報] ページで、クライアント ID をメモします。
> 
> 3. Cloud IAP で保護されたクライアント ID の OIDC トークンをリクエストします。
> 4. Authorization: Bearer ヘッダーに OIDC トークンを追加して、Cloud IAP で保護されたリソースへの認証済みリクエストを作成します。

分からん、、、

具体的には（３）が分かりません。 え？ 何のこと？ ってなります。

## Cloud IAP のサービスアカウント認証
試行錯誤の結果、なんとか認証を通すことができました。忘れないように手順を書いておきます。
### 前提　AppEngine で IAPアクセス制御が有効であること
AppEngine ダッシュボードの設定画面下部に有効化ボタンがあります。

### 準備 １　Cloud IAP でサービスアカウントを許可設定
1. https://console.cloud.google.com/security/iap にアクセス
2. リソースを選択して[IAP-secured Web App User]に許可したいサービスアカウントを追加

### 準備 ２　サービスアカウントの秘密鍵をダウンロード
1. https://console.cloud.google.com/apis/credentials にアクセス
2. [認証情報を作成]→[サービスアカウント]→[JSON] を選択してダウンロード
3. ダウンロードしたキーファイル内の情報を大切に保存しておく

秘密鍵のダウンロードは１度しかできません。紛失した場合は新規に生成する必要があります。また、セキュリティ情報なので取り扱いに注意し、不要になったものは失効させておくことをお勧めします。

### 準備 ３　クライアントIDを作成してメモ
1. https://console.cloud.google.com/apis/credentials にアクセス
2. [認証情報を作成]→[OAuthクライアントID]→[ウェブアプリケーション] を選択して生成
3. クライアントIDをメモしておく

### 認証手順 １　サービスアカウントのJWTトークンを生成する
JWTトークンについては https://jwt.io/ を参照してください。

* ヘッダー
    * `alg` : `RS256`
    * `kid` : キーファイル内の`private_key_id`の値
* ペイロード
    * `aud` : `https://www.googleapis.com/oauth2/v4/token`,
    * `iss` : キーファイル内の`client_email`の値
    * `sub` : キーファイル内の`client_email`の値（上と同じ）
    * `iaｔ` : トークンの生成時刻
    * `exp` : トークンの生成時刻 + 有効期限（60分以内）
    * `target_audience` ： メモしておいたクライアントID
* シグニチャ
    * 署名 ： キーファイル内の`private_key`を使って署名する

### 認証手順 ２ トークンエンドポイントにOIDCトークンをリクエスト

* URL : `https://www.googleapis.com/oauth2/v4/token`
* メソッド : POST
* リクエストボディ
    * `grant_type` : `urn:ietf:params:oauth:grant-type:jwt-bearer`
    * `assertion` : 先程生成したJWTトークン
* レスポンスボディ
    * `id_token` : 次に使うOIDCトークン

### 認証手順 3 OIDCトークンを使用してBearer認証

* URL : Cloud AIP アクセス制限した AppEngine の URL
* リクエストヘッダー
    * `Authorization` : `Bearer` + 半角スペース + 先程取得したOIDCトークン 
* リクエストボディ
    * お好みでどうぞ

## 感想
ドキュメントに書いてない情報が多すぎます。。。

各パラメータ値はサンプルコードから抽出してきたのですが、トークンエンドポイントのURLだけは、疑問が残っていて、`https://www.googleapis.com/oauth2/v3/token`や`https://oauth2.googleapis.com/token`に変更しても、うまく動くようです。

どれが正解なのでしょうか、、、、

OIDC周りのURLの違いについて書いてある資料とかは見つけられませんでした。詳しい方がいましたら教えていただきたいです。