CI部の北脇です。
Google Apps Script (GAS) から Identity-Aware Proxy(IAP) で保護された Google App Engine (GAE) にリクエストを送信する方法を紹介します。
具体的には、サービスアカウントを使用して認証し、リクエストを送信する方法を取り上げます。
nestJSで作成したプログラムをGAEにデプロイします。
APIとしては「/api/test」にPOSTリクエストを送信すると"Hello World!"をレスポンスする簡単ななものとなっています。
上記アプリケーションがGAE上にデプロイします。
次にIAPの設定になります。
コンソールからIAPの画面に遷移しGAEが保護されていることを確認します。
IAPの画面からOAuthの構成画面に遷移しクライアントIDを取得します。
これでGAEとIAPの設定は完了しました。
サービスアカウントを作成します。
ロールとしては「IAPで保護されたウェブアプリユーザー」を選択します。
サービスアカウント作成後ダウンロードします。
これでGoogle Cloud側の準備は完了しました。
以下のスクリプトはGoogle Apps Script (GAS) から Google App Engine (GAE) にPOSTリクエストを送るものです。
ただし、この状態ではIdentity-Aware Proxy (IAP) で保護された GAEに対してPOSTリクエストが送信できる認証情報が何も設定されていません。function myFunction() {
const apiUrl = 'https://{GAEのドメイン}/api/test';
try{
const options = {
"method": "post",
};
let res = UrlFetchApp.fetch(apiUrl, options)
let data = res.getContentText('UTF-8')
console.log(data)
}catch(e){
console.log(e)
}
}
このままでは401エラー(Unauthorized)が返されます。
エラーメッセージは以下の通りです。{ [Exception: Request failed for https://{GAEのドメイン} returned code 401. Truncated server response: Invalid IAP credentials: empty token (use muteHttpExceptions option to examine full response)] name: 'Exception' }
そこで、前述で作成したサービスアカウントを利用してHTTPリクエストを送信できるように、このプログラムを修正します。
サービスアカウントとIAPのクライアントIDからIDトークンを取得し、APIリクエスト時にAuthorizationヘッダを付けることで、IAPで保護されたGAEに対してリクエストを送信することが可能になります。PRIVATE_KEY = "{サービスアカウントのプライベートキー}"
PRIVATE_KEY_ID = "{サービスアカウントのプライベートキーID}"
CLIENT_EMAIL = "サービスアカウントのEメール";
CLIENT_ID = "{IAPのクライアントID}";
function myFunction() {
//ApplwWatchRawテーブル用のAPIのエンドポイント
const apiUrl = 'https://{GAEのドメイン}/api/test';
var headers = {
'Authorization': 'Bearer ' + getIdToken()
}
try{
const options = {
"headers": headers,
"method": "post",
};
let res = UrlFetchApp.fetch(apiUrl, options)
let data = res.getContentText('UTF-8')
console.log(data)
}catch(e){
console.log(e)
}
}
function getIdToken() {
const options = {
"method": "POST",
"payload": {
"grant_type": 'urn:ietf:params:oauth:grant-type:jwt-bearer',
"assertion": getAssertion()
},
'muteHttpExceptions': true,
};
const response = JSON.parse(UrlFetchApp.fetch('https://oauth2.googleapis.com/token', options));
console.log(response.id_token)
return response.id_token;
}
function getAssertion() {
const privateKey = PRIVATE_KEY
const header = {
alg: 'RS256',
typ: 'JWT',
kid:PRIVATE_KEY_ID
};
const now = new Date();
const claimSet = {
iss: CLIENT_EMAIL,
aud: "https://www.googleapis.com/oauth2/v4/token",
exp: (now.getTime() / 1000) + 3000,
iat: now.getTime() / 1000,
target_audience: CLIENT_ID
};
let toSign = Utilities.base64EncodeWebSafe(JSON.stringify(header)) + '.' + Utilities.base64EncodeWebSafe(JSON.stringify(claimSet));
toSign = toSign.replace(/=+$/, '');
const signatureBytes = Utilities.computeRsaSha256Signature(toSign, privateKey);
let signature = Utilities.base64EncodeWebSafe(signatureBytes);
signature = signature.replace(/=+$/, '');
return toSign + '.' + signature;
};
サービスアカウントからプライベートキー、プライベートキーID、Eメールアドレスを設定します。
また、IAPのコンソール画面に記載されていたクライアントIDを設定します。
getIdtoken関数でIDトークンを取得しています。
上記サービスアカウントの情報やクライアントIDを利用して「oauth2.googleapis.com/token」にリクエストを送信します。
修正後再度GASを実行しましょう。
無事IAPで保護されたGAEに対してリクエストが通ることを確認できました!
Google Cloud、Google Workspaceに関する お問い合わせはこちら
XIMIX(サイミクス)は商標登録出願中です