ユーザーの認証だけでなく、他のサービスに 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 Console と gcloud
コマンドライン ツールを使用して、サービス アカウントと秘密鍵ファイルを作成し、サービス アカウントにサービス アカウント トークンの作成者役割を割り当てる方法を説明します。この操作を API で行う方法については、サービス アカウントの作成と管理をご覧ください。
サービス アカウントとキーを作成するには:
Google Cloud コンソール
サービス アカウントの作成:
Google Cloud コンソールで [サービス アカウントの作成] ページに移動します。
使用するプロジェクトを選択します。
[サービス アカウント名] フィールドに名前を入力します。
省略可: [サービス アカウントの説明] フィールドに説明を入力します。
[作成] をクリックします。
[ロールを選択] フィールドをクリックします。[すべてのロール] で、[サービス アカウント] > [サービス アカウント トークン作成者] を選択します。
[完了] をクリックします。
ブラウザ ウィンドウは閉じないでください。次のステップでこれを使用します。
サービス アカウント キーを作成します。
- Google Cloud コンソールで、作成したサービス アカウントのメールアドレスをクリックします。
- [キー] をクリックします。
- [鍵を追加]、[新しい鍵を作成] の順にクリックします。
- [作成] をクリックします。サービス アカウントの秘密鍵を含む JSON ファイルがパソコンにダウンロードされます。
- [閉じる] をクリックします。
gcloud
ローカルマシン上の Google Cloud CLI を使用して以下のコマンドを実行します。このコマンドは、Cloud Shell 内でも実行できます。
gcloud
にデフォルトのアカウントを設定する。アカウントが複数ある場合は、使用する Google Cloud プロジェクトのアカウントを選択してください。gcloud auth login
Google 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
は、キーファイルに使用する名前に置き換えます。デフォルトでは、gcloud
JSON ファイルが作成されます。gcloud iam service-accounts keys create FILE_NAME.json \ --iam-account SA_EMAIL_ADDRESS
詳しくは gcloud
リファレンスをご覧ください。
秘密鍵を保護する方法については、認証情報を管理する際のベスト プラクティスをご覧ください。
認証をサポートするように API を構成する
ESP が署名済みの JWT でクレームを検証できるように、OpenAPI ドキュメントにセキュリティ要件オブジェクトとセキュリティ定義オブジェクトが設定されている必要があります。
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 内の"aud"
(オーディエンス)クレームの形式がhttps://SERVICE_NAME
である必要があります。ここで、SERVICE_NAME はフラグ--disable_jwt_audience_service_name_check
が使用されていない限り、OpenAPI ドキュメントのhost
フィールドで構成した Endpoints サービスの名前です。このフラグが使用されていて、x-google-audiences
が指定されていない場合、JWTaud
フィールドの確認は行われていません。必要に応じて、
x-google-jwt-locations
にsecurityDefinitions
セクションを追加してください。この値を使用して、カスタム JWT の場所を定義できます。デフォルトの JWT の場所は、Authorization
ヘッダー(接頭辞「Bearer」)、X-Goog-Iap-Jwt-Assertion
ヘッダー、またはaccess_token
クエリ パラメータです。注:x-google-jwt-locations
を指定すると、Endpoints はデフォルトの場所をすべて無視します。x-google-jwt-locations
は ESPv2 でのみサポートされます。
API 全体に適用するには、ファイルの最上位に
security
セクションを追加します。インデントされない場合は、特定のメソッドに適用します。API レベルとメソッドレベルの両方でsecurity
セクションを使用する場合、メソッドレベルの設定は API レベルの設定より優先されます。security: - DEFINITION_NAME: []
securityDefinitions
セクションで使用した名前はDEFINITION_NAME
と置き換えます。securityDefinitions
セクションに定義が複数ある場合は、それらをsecurity
に追加します。例を次に示します。security: - service-1: [] - service-2: []
更新された OpenAPI ドキュメントをデプロイします。
OPENAPI_DOC
は、OpenAPI ドキュメントの名前に置き換えます。gcloud endpoints services deploy OPENAPI_DOC
ESP がリクエストを API に転送する前に、ESP は次のことを確認します。
- OpenAPI ドキュメントの
x-google-jwks_uri
フィールドで指定された URI にある公開鍵を使用した JWT の署名。 - その
"iss"
JWT での発行者のリクエストが、x-google-issuer
フィールドに入力します。 - その
"aud"
JWT 内の Endpoints サービス名が含まれているか、またはx-google-audiences
フィールドに入力します。 - トークンが期限切れになっていないことを確認するには、
"exp"
(有効期限)クレームを使用します。
x-google-issuer
、x-google-jwks_uri
、x-google-audiences
、x-google-jwt-locations
の詳細については、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 の取り扱いをご覧ください。