呼び出しの認証

関数でその他の管理アクションを作成、更新、実行するには、適切なロールを持つプリンシパルが必要です。詳しくは、IAM によるアクセスの承認をご覧ください。

ただし、関数の呼び出しはより複雑なイベントになる場合があります。イベント ドリブン関数を呼び出すことができるのは、関数が登録されているイベントソースだけですが、HTTP 関数は、異なる場所で発生するさまざまな種類の ID によって呼び出すことができます。呼び出し元は、関数やその関数を使用したい別の関数またはサービスをテストするデベロッパーである可能性があります。デフォルトでは、これらの ID で自身を認証する(自身の ID を証明する)とともに、適切な権限が必要です。未認証アクセスは可能ですが、有効にする必要があります。詳しくは、IAM によるアクセス管理をご覧ください。

デベロッパー テストの認証

デベロッパーは、関数を作成、更新、削除するためのアクセス権が必要になります。また、アクセス権は通常の(IAM)プロセスを使用して付与されます。

ただし、デベロッパーは、テスト目的で関数を呼び出す必要がある場合もあります。curl などのツールを使用して関数を呼び出すには、次の操作を行う必要があります。

  • Cloud Functions へのアクセスに使用しているアカウントに、cloudfunctions.functions.invoke 権限を含むロールを割り当てます。デフォルトでは、Cloud Functions 管理者と Cloud Functions デベロッパーのロールにこの権限が含まれています。ロールとそれに関連する権限の詳細については、Cloud Functions IAM ロールをご覧ください。

  • ローカルマシンから作業している場合は、Google Cloud SDK を初期化してコマンドライン アクセスを設定します。サービス アカウント キーをダウンロードし、GOOGLE_APPLICATION_CREDENTIALS 環境変数を設定したことを確認します。

  • リクエストに、Authorization ヘッダーに保存されている Google 生成の ID トークンとして認証情報を指定します。たとえば、次のように gcloud でトークンを取得します。

    curl https://REGION-PROJECT_ID.cloudfunctions.net/FUNCTION_NAME \
      -H "Authorization: bearer $(gcloud auth print-identity-token)"
    

通常どおり、関数の開発と使用に必要な最小限の権限セットを割り当てることをおすすめします。関数の IAM ポリシーは、必要最小限のユーザーとサービス アカウントに限定してください。

関数呼び出しに対する関数の認証

複数の関数を接続するサービスをビルドする場合は、それぞれの関数が他の関数の特定のサブセットにのみリクエストを送信できるようにすることをおすすめします。たとえば、login 関数の場合、user profiles 関数へのアクセスは許可しますが、search 関数へのアクセスは許可しません。

特定の呼び出し側関数からのリクエストを受信するように受信側関数を構成するには、受信側の関数で、呼び出し側関数のサービス アカウントに Cloud Functions 起動元(roles/cloudfunctions.invoker)ロールを付与する必要があります。

Console

  1. Google Cloud Console に移動します。

    Google Cloud Console に移動

  2. 受信側関数の横にあるチェックボックスをオンにします。

  3. 画面の上部の [権限] をクリックします。[権限] パネルが開きます。

  4. [プリンシパルを追加] をクリックします。

  5. [新しいプリンシパル] フィールドに、呼び出し側関数の ID を入力します。これはサービス アカウントのメールアドレスです。

  6. [役割を選択] プルダウン メニューから Cloud Functions > Cloud Functions 起動元 役割を選択します。

  7. [保存] をクリックします。

gcloud

gcloud functions add-iam-policy-binding コマンドを使用します。

gcloud functions add-iam-policy-binding RECEIVING_FUNCTION \
  --member='serviceAccount:CALLING_FUNCTION_IDENTITY' \
  --role='roles/cloudfunctions.invoker'

ここで、RECEIVING_FUNCTION は受信側関数の名前、CALLING_FUNCTION_IDENTITY は呼び出し側関数の ID(サービス アカウントのメールアドレス)です。

この呼び出し側関数は受信側関数を呼び出すため、認証用に Google によって署名された ID トークンも備えている必要があります。これには、次の 2 つの手順を行います。

  1. audience フィールド(aud)に受信側関数の URL を設定して、Google によって署名された ID トークンを作成します。

  2. 関数に対するリクエストの Authorization: Bearer ID_TOKEN ヘッダーに ID トークンを含めます。

このプロセスを管理する最も簡単かつ信頼性の高い方法は、以下に示すように認証ライブラリを使用してこのトークンを生成し、適用することです。

プログラムによるトークンの生成

次のコードを使用して ID トークンを生成できます。このコードは、ライブラリが認証情報を取得できる環境で動作します。これには、ローカルのアプリケーションのデフォルト認証情報をサポートする環境も含まれます。

Node.js

/**
 * TODO(developer): Uncomment these variables before running the sample.
 */
// const url = 'https://TARGET_URL';
// let targetAudience = null;
const {GoogleAuth} = require('google-auth-library');
const auth = new GoogleAuth();

async function request() {
  if (!targetAudience) {
    // Use the request URL hostname as the target audience for requests.
    const {URL} = require('url');
    targetAudience = new URL(url);
  }
  console.info(`request ${url} with target audience ${targetAudience}`);
  const client = await auth.getIdTokenClient(targetAudience);
  const res = await client.request({url});
  console.info(res.data);
}

request().catch(err => {
  console.error(err.message);
  process.exitCode = 1;
});

Python

import urllib

import google.auth.transport.requests
import google.oauth2.id_token

def make_authorized_get_request(service_url):
    """
    make_authorized_get_request makes a GET request to the specified HTTP endpoint
    in service_url (must be a complete URL) by authenticating with the
    ID token obtained from the google-auth client library.
    """

    req = urllib.request.Request(service_url)

    auth_req = google.auth.transport.requests.Request()
    id_token = google.oauth2.id_token.fetch_id_token(auth_req, service_url)

    req.add_header("Authorization", f"Bearer {id_token}")
    response = urllib.request.urlopen(req)

    return response.read()

Go


import (
	"context"
	"fmt"
	"io"

	"google.golang.org/api/idtoken"
)

// makeGetRequest makes a request to the provided targetURL with an authenticated client.
func makeGetRequest(w io.Writer, targetURL string) error {
	// functionURL := "https://TARGET_URL"
	ctx := context.Background()

	// client is a http.Client that automatically adds an "Authorization" header
	// to any requests made.
	client, err := idtoken.NewClient(ctx, targetURL)
	if err != nil {
		return fmt.Errorf("idtoken.NewClient: %v", err)
	}

	resp, err := client.Get(targetURL)
	if err != nil {
		return fmt.Errorf("client.Get: %v", err)
	}
	defer resp.Body.Close()
	if _, err := io.Copy(w, resp.Body); err != nil {
		return fmt.Errorf("io.Copy: %v", err)
	}

	return nil
}

Java

import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpResponse;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.auth.http.HttpCredentialsAdapter;
import com.google.auth.oauth2.GoogleCredentials;
import com.google.auth.oauth2.IdTokenCredentials;
import com.google.auth.oauth2.IdTokenProvider;
import java.io.IOException;

public class Authentication {

  // makeGetRequest makes a GET request to the specified Cloud Run or
  // Cloud Functions endpoint, serviceUrl (must be a complete URL), by
  // authenticating with an Id token retrieved from Application Default Credentials.
  public static HttpResponse makeGetRequest(String serviceUrl) throws IOException {
    GoogleCredentials credentials = GoogleCredentials.getApplicationDefault();
    if (!(credentials instanceof IdTokenProvider)) {
      throw new IllegalArgumentException("Credentials are not an instance of IdTokenProvider.");
    }
    IdTokenCredentials tokenCredential =
        IdTokenCredentials.newBuilder()
            .setIdTokenProvider((IdTokenProvider) credentials)
            .setTargetAudience(serviceUrl)
            .build();

    GenericUrl genericUrl = new GenericUrl(serviceUrl);
    HttpCredentialsAdapter adapter = new HttpCredentialsAdapter(tokenCredential);
    HttpTransport transport = new NetHttpTransport();
    HttpRequest request = transport.createRequestFactory(adapter).buildGetRequest(genericUrl);
    return request.execute();
  }
}

手動でのトークンの生成

関数呼び出しの際になんらかの理由で認証ライブラリを使用できない場合、ID トークンを手動で取得するには、Compute Metadata Server を使用する方法と、自己署名 JWT を作成して Google によって署名された ID トークンと交換する方法の 2 つがあります。

メタデータ サーバーの使用

次のように、Compute Metadata Server を使用して、特定のユーザーの ID トークンを取得します。

curl "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=[AUDIENCE]" \
     -H "Metadata-Flavor: Google"

ここで、AUDIENCE は呼び出す関数の URL(https://GCP_REGION-PROJECT_ID.cloudfunctions.net/my-function など)です。

Google によって署名された ID トークンと自己署名 JWT の交換

  1. 受信側の関数で、呼び出し側関数のサービス アカウントに Cloud Functions 起動元(roles/cloudfunctions.invoker)ロールを付与します。

  2. サービス アカウントとキーを作成し、呼び出し側関数またはサービスがリクエストを発信するホストに、秘密鍵(JSON 形式)を含むファイルをダウンロードします。

  3. ヘッダーを {"alg":"RS256","typ":"JWT"} に設定して JWT を作成します。ペイロードには、受信側関数の URL に設定された target_audience クレームと、上述のサービス アカウントのメールアドレスに設定された iss クレームと sub クレームを含める必要があります。また、exp クレームと iat クレームも含める必要があります。aud クレームは https://www.googleapis.com/oauth2/v4/token に設定する必要があります。

  4. 先ほどダウンロードした秘密鍵を使用して、JWT に署名します。

  5. この JWT を使用して、https://www.googleapis.com/oauth2/v4/token に POST リクエストを送信します。認証データはリクエストのヘッダーと本文に含める必要があります。

    ヘッダー内:

    Authorization: Bearer $JWT - where $JWT is the JWT you just created
    Content-Type: application/x-www-form-urlencoded
    

    本文内:

    grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=$JWT
    

    $JWT は、作成した JWT で置き換えます

    これにより、Google によって署名された id_token を含む別の JWT が返されます。

GET/POST リクエストを受信側関数に送信します。Google によって署名された ID トークンをリクエストの Authorization: Bearer ID_TOKEN_JWT ヘッダーに含めます。