ユーザーの認証だけでなく、他のサービスに API の使用を許可する場合があります。ユーザーが認証情報を送信できるように、クライアント アプリケーションでウェブログイン プロンプトを表示することがありますが、サービス間で安全な通信を行うには別の手段が必要になります。このページでは、サービス間で認証を実装する際のおすすめの方法とサンプルコードを紹介します。
概要
API にリクエストを送信するサービスを識別するには、サービス アカウントを使用します。呼び出し元のサービスは、サービス アカウントの秘密鍵を使用して安全な JSON Web Token(JWT)に署名し、署名した JWT をリクエストに含めて API に送信します。
API と呼び出し側のサービスにサービス間認証を実装するには、次の手順に従います。
- 呼び出し側のサービスのサービス アカウントとキーを作成します。
- Cloud Endpoints サービスの OpenAPI ドキュメントに認証サポートを追加します。
次の処理を行うコードを呼び出し側のサービスに追加します。
- JWT を作成し、サービス アカウントの秘密鍵で署名する。
- リクエストで署名済みの JWT を API に送信する。
ESP は、リクエストを API に転送する前に、JWT のクレームが OpenAPI ドキュメントの構成と一致することを検証します。ESP は、サービス アカウントに付与されている Cloud Identity の権限を確認しません。
前提条件
すでに以下を行っていることを前提としています。
サービス アカウントとキーを作成する
呼び出しサービスが JWT の署名に使用する秘密鍵ファイルがあるサービス アカウントが必要です。API にリクエストを送信するサービスが複数ある場合は、1 つのサービス アカウントを作成してすべての呼び出しサービスを表すことができます。サービスによって権限が異なる場合など、サービスを区別する必要がある場合は、呼び出し側のサービスごとにサービス アカウントとキーを作成します。
このセクションでは、 Google Cloud コンソールと gcloud コマンドライン ツールを使用して、サービス アカウントと秘密鍵ファイルを作成し、サービス アカウントにサービス アカウント トークンの作成者役割を割り当てる方法を説明します。この操作を API で行う方法については、サービス アカウントの作成と管理をご覧ください。
サービス アカウントとキーを作成するには:
Google Cloud コンソール
サービス アカウントの作成:
Google Cloud コンソールで、[サービス アカウントの作成] ページに移動します。
使用するプロジェクトを選択します。
[サービス アカウント名] フィールドに名前を入力します。
省略可: [サービス アカウントの説明] フィールドに説明を入力します。
[作成] をクリックします。
[完了] をクリックします。
ブラウザ ウィンドウを閉じないでください。次のステップでこれを使用します。
サービス アカウント キーを作成します。
- Google Cloud コンソールで、作成したサービス アカウントのメールアドレスをクリックします。
- [キー] をクリックします。
- [鍵を追加]、[新しい鍵を作成] の順にクリックします。
- [作成] をクリックします。サービス アカウントの秘密鍵を含む JSON ファイルがパソコンにダウンロードされます。
- [閉じる] をクリックします。
gcloud
ローカルマシン上の Google Cloud CLI を使用して以下のコマンドを実行します。このコマンドは、Cloud Shell 内でも実行できます。
gcloudにデフォルトのアカウントを設定する。アカウントが複数ある場合は、使用する Google Cloud プロジェクトのアカウントを選択してください。gcloud auth loginGoogle Cloud プロジェクトのプロジェクト ID を表示します。
gcloud projects listデフォルト プロジェクトを設定します。
PROJECT_IDは、使用する Google Cloud プロジェクト ID に置き換えます。gcloud config set project PROJECT_ID
サービス アカウントを作成します。
SA_NAMEとSA_DISPLAY_NAMEは、それぞれ使用する名前と表示名に置き換えます。gcloud iam service-accounts create SA_NAME \ --display-name "SA_DISPLAY_NAME"
作成したサービス アカウントのメールアドレスを表示します。
gcloud iam service-accounts listサービス アカウント トークン作成者の役割を追加します。
SA_EMAIL_ADDRESSは、サービス アカウントのメールアドレスに置き換えます。gcloud projects add-iam-policy-binding PROJECT_ID \ --member serviceAccount:SA_EMAIL_ADDRESS \ --role roles/iam.serviceAccountTokenCreator
現在の作業ディレクトリにサービス アカウント キー ファイルを作成します。
FILE_NAMEは、キーファイルに使用する名前に置き換えます。デフォルトでは、gcloudJSON ファイルが作成されます。gcloud iam service-accounts keys create FILE_NAME.json \ --iam-account SA_EMAIL_ADDRESS
詳しくは gcloud リファレンスをご覧ください。
秘密鍵を保護する方法については、認証情報を管理する際のベスト プラクティスをご覧ください。
認証をサポートするように API を構成する
ゲートウェイを呼び出すサービスのサービス アカウント認証を有効にするには、ESP が署名済みの JWT でクレームを検証できるように、OpenAPI ドキュメントのセキュリティ オブジェクトを変更します。変更内容は、使用する OpenAPI 仕様のバージョンによって異なります。
OpenAPI 2.0
- OpenAPI 仕様の発行元として、サービス アカウントを追加します。
securityDefinitions: DEFINITION_NAME: authorizationUrl: "" flow: "implicit" type: "oauth2" x-google-issuer: "SA_EMAIL_ADDRESS" x-google-jwks_uri: "https://www.googleapis.com/robot/v1/metadata/x509/SA_EMAIL_ADDRESS"
DEFINITION_NAMEは、このセキュリティ定義を識別する文字列に置き換えます。サービス アカウント名または呼び出し側のサービスを識別する名前に置き換えることをおすすめします。SA_EMAIL_ADDRESSは、サービス アカウントのメールアドレスに置き換えます。- OpenAPI 仕様では複数のセキュリティ定義を記述できますが、定義ごとに、異なる
x-google-issuerが必要です。呼び出し側のサービスごとに別々のサービス アカウントを作成した場合、サービス アカウントごとにセキュリティ定義を作成できます。次に例を示します。securityDefinitions: service-1: authorizationUrl: "" flow: "implicit" type: "oauth2" x-google-issuer: "service-1@example-project-12345.iam.gserviceaccount.com" x-google-jwks_uri: "https://www.googleapis.com/robot/v1/metadata/x509/service-1@example-project-12345.iam.gserviceaccount.com" service-2: authorizationUrl: "" flow: "implicit" type: "oauth2" x-google-issuer: "service-2@example-project-12345.iam.gserviceaccount.com" x-google-jwks_uri: "https://www.googleapis.com/robot/v1/metadata/x509/service-2@example-project-12345.iam.gserviceaccount.com"
- 必要に応じて、
x-google-audiencesにsecurityDefinitionsセクションを追加してください。x-google-audiencesを追加しない場合、ESP を使用するには、JWT にhttps://SERVICE_NAME形式の"aud"(audience)クレームが必要になります。SERVICE_NAME は、OpenAPI ドキュメントのhostフィールドで構成した ESP サービスの名前です。 - API 全体に適用するには、ファイルの最上位に
securityセクションを追加します。インデントされない場合は、特定のメソッドに適用します。securityセクションを API レベルとメソッドレベルの両方で指定した場合、API レベルの設定よりもメソッドレベルの設定が優先されます。security: - DEFINITION_NAME: []
securityDefinitionsセクションで使用した名前はDEFINITION_NAMEと置き換えます。securityDefinitionsセクションに定義が複数ある場合は、それらをsecurityに追加します。例を次に示します。security: - service-1: [] - service-2: []
- 更新された OpenAPI 仕様をデプロイします。ESP がリクエストを API に転送する前に、ESP は次のことを確認します。
- OpenAPI 仕様の
x-google-jwks_uriフィールドで指定された URI にある公開鍵を使用した JWT の署名。 - JWT の
"iss"(issuer)クレームがx-google-issuerフィールドの値と一致していること。 - その
"aud"JWT 内の ESP サービス名が含まれているか、またはx-google-audiencesフィールドに入力します。 - トークンが期限切れになっていないことを確認するには、
"exp"(有効期限)クレームを使用します。
- OpenAPI 仕様の
OpenAPI 3.x
- OpenAPI 仕様の発行元として、サービス アカウントを追加します。
components: securitySchemes: SCHEME_NAME: type: oauth2 flows: implicit: authorizationUrl: "" scopes: {} x-google-auth: issuer: SA_EMAIL_ADDRESS jwksUri: https://www.googleapis.com/robot/v1/metadata/x509/SA_EMAIL_ADDRESS audiences: - 848149964201.apps.googleusercontent.com - 841077041629.apps.googleusercontent.com jwtLocations: - header: Authorization valuePrefix: "Bearer " security: - SCHEME_NAME: []
SCHEME_NAMEは、このセキュリティ スキームを識別する文字列に置き換えます。サービス アカウント名または呼び出し側のサービスを識別する名前に置き換えることをおすすめします。SA_EMAIL_ADDRESSは、サービス アカウントのメールアドレスに置き換えます。- OpenAPI 仕様では複数のセキュリティ スキームを定義できますが、定義ごとに異なる
issuerが必要です。呼び出し側のサービスごとに別々のサービス アカウントを作成した場合、サービス アカウントごとにセキュリティ定義を作成できます。次に例を示します。components: securitySchemes: service-1: type: oauth2 flows: implicit: authorizationUrl: "" scopes: {} x-google-auth: issuer: "service-1@example-project-12345.iam.gserviceaccount.com" jwksUri: https://www.googleapis.com/robot/v1/metadata/x509/service-1@example-project-12345.iam.gserviceaccount.com jwtLocations: - header: Authorization valuePrefix: "Bearer " service-2: type: oauth2 flows: implicit: authorizationUrl: "" scopes: {} x-google-auth: issuer: "service-2@example-project-12345.iam.gserviceaccount.com" jwksUri: "https://www.googleapis.com/robot/v1/metadata/x509/service-2@example-project-12345.iam.gserviceaccount.com" jwtLocations: - header: Authorization valuePrefix: "Bearer "
- 必要に応じて、
audiencesにsecuritySchemesセクションを追加してください。audiencesを追加しない場合、ESP を使用するには、JWT にhttps://SERVICE_NAME形式の"aud"(audience)クレームが必要になります。SERVICE_NAMEは、OpenAPI ドキュメントのhostフィールドで構成した ESP サービスの名前です。 - API 全体に適用するには、ファイルの最上位に
securityセクションを追加します。インデントされない場合は、特定のメソッドに適用します。securityセクションを API レベルとメソッドレベルの両方で指定した場合、API レベルの設定よりもメソッドレベルの設定が優先されます。security: - SCHEME_NAME: []
securitySchemesセクションで使用した名前は SCHEME_NAME と置き換えます。securitySchemesセクションに定義が複数ある場合は、それらをsecurityに追加します。例を次に示します。security: - service-1: [] - service-2: []
- 更新された OpenAPI 仕様をデプロイします。ESP がリクエストを API に転送する前に、ESP は次のことを確認します。
- OpenAPI 仕様の
jwksUriフィールドで指定された URI にある公開鍵を使用した JWT の署名。 - JWT の
"iss"(issuer)クレームがissuerフィールドの値と一致していること。 - その
"aud"JWT 内の ESP サービス名が含まれているか、またはaudiencesフィールドに入力します。 - トークンが期限切れになっていないことを確認するには、
"exp"(有効期限)クレームを使用します。
- OpenAPI 仕様の
認証されたリクエストを Endpoints API に送信する
認証されたリクエストを行う場合、呼び出し側のサービスは、OpenAPI ドキュメントに指定されたサービス アカウントで署名された JWT を送信します。呼び出し側のサービスは、次の処理を行う必要があります。
- JWT を作成し、サービス アカウントの秘密鍵で署名します。
- リクエストで署名済みの JWT を API に送信します。
次のサンプルコードは、このプロセスを一部の言語で示しています。他の言語で認証済みリクエストを行うには、jwt.io で、サポートされているライブラリの一覧を参照してください。
- 呼び出し側のサービスに以下の関数を追加し、次のパラメータを渡します。
Java saKeyfile: サービス アカウントの秘密鍵ファイルへのフルパス。saEmail: サービス アカウントのメールアドレス。-
audience:x-google-audiencesフィールドを OpenAPI ドキュメントに追加すると、x-google-audiencesで指定したいずれかの値にaudienceを設定します。それ以外の場合は、audience~https://SERVICE_NAMEを設定します。ここで、SERVICE_NAMEは、Endpoints のサービス名です。 expiryLength: JWT の有効期限(秒)。
Python -
sa_keyfile: サービス アカウントの秘密鍵ファイルへのフルパス。 -
sa_email: サービス アカウントのメールアドレス。 -
audience:x-google-audiencesフィールドを OpenAPI ドキュメントに追加すると、x-google-audiencesで指定したいずれかの値にaudienceを設定します。それ以外の場合は、audience~https://SERVICE_NAMEを設定します。ここで、SERVICE_NAMEは、Endpoints のサービス名です。 expiry_length: JWT の有効期限(秒)。
Go saKeyfile: サービス アカウントの秘密鍵ファイルへのフルパス。-
saEmail: サービス アカウントのメールアドレス。 -
audience:x-google-audiencesフィールドを OpenAPI ドキュメントに追加すると、x-google-audiencesで指定したいずれかの値にaudienceを設定します。それ以外の場合は、audience~https://SERVICE_NAMEを設定します。ここで、SERVICE_NAMEは、Endpoints のサービス名です。 expiryLength: JWT の有効期限(秒)。
この関数は JWT を作成して秘密鍵ファイルで署名し、署名済みの JWT を返します。
Java Python Go - 呼び出し側のサービスに、以下の関数を追加し、署名付き JWT を
Authorization: Bearerヘッダー内で API に送信します。Java Python Go
JWT を使用してリクエストを送信する場合は、セキュリティ上の理由から、認証トークンを Authorization: Bearer ヘッダーに入れることをおすすめします。次に例を示します。
curl --request POST \
--header "Authorization: Bearer ${TOKEN}" \
"${ENDPOINTS_HOST}/echo"
ENDPOINTS_HOST と TOKEN はそれぞれ、API のホスト名と認証トークンを格納する環境変数です。
API での認証結果の受信
通常、ESP は受信したすべてのヘッダーを転送します。ただし、バックエンド アドレスが OpenAPI 仕様の x-google-backend または gRPC サービス構成の BackendRule で指定されている場合は、元の Authorization ヘッダーより優先します。
ESP は認証結果を X-Endpoint-API-UserInfo でバックエンド API に送信します。元の Authorization ヘッダーではなく、このヘッダーを使用することをおすすめします。このヘッダーは、base64url が JSON オブジェクトをエンコードする文字列です。JSON オブジェクトの形式は ESPv2 と ESP で異なります。ESPv2 では、JSON オブジェクトは元の JWT ペイロードになります。ESP では、JSON オブジェクトは異なるフィールド名を使用し、元の JWT ペイロードを claims フィールドに配置します。形式の詳細については、バックエンド サービスにおける JWT の取り扱いをご覧ください。