サーバー間アプリケーションに OAuth 2.0 を使用する

Google OAuth 2.0 システムは、ウェブ アプリケーションと Google サービス間など、サーバー間のインタラクションをサポートしています。このシナリオでは、サービス アカウントが必要です。これは、個々のエンドユーザーではなく、アプリケーションに属するアカウントです。サービス アカウントの代わりにアプリケーションによって Google API が呼び出されるため、ユーザーが直接関与することはありません。このシナリオは「2 段階認証 OAuth」または「2LO」と呼ばれることもあります。(関連する用語の「3-legged OAuth」は、アプリケーションがエンドユーザーに代わって Google API を呼び出し、ユーザーの同意が必要な場合のシナリオを指します)。

通常、アプリケーションが Google API を使用してユーザーのデータではなく独自のデータを使用する場合は、サービス アカウントを使用します。たとえば、データの永続化に Google Cloud Datastore を使用するアプリケーションでは、サービス アカウントを使用して Google Cloud Datastore API への呼び出しを認証します。

Google Workspace ドメイン管理者は、ドメイン内のユーザーに代わってユーザーデータにアクセスできるように、サービス アカウントにドメイン全体の権限を付与することもできます。

このドキュメントでは、Google API クライアント ライブラリ(推奨)または HTTP を使用して、アプリケーションがサーバー間 OAuth 2.0 フローを完了する方法について説明します。

概要

サーバー間のインタラクションをサポートするには、まず でプロジェクトのサービス アカウントを作成します。Google Workspace アカウントのユーザーのユーザーデータにアクセスする場合は、サービス アカウントにドメイン全体のアクセス権を委任します。

次に、アプリケーションは、サービス アカウントの認証情報を使用して OAuth 2.0 認証サーバーからアクセス トークンをリクエストし、承認された API 呼び出しを行う準備を行います。

最後に、アプリケーションはアクセス トークンを使用して Google API を呼び出します。

サービス アカウントの作成

サービス アカウントの認証情報には、一意の生成されたメールアドレスと、少なくとも 1 つの公開鍵と秘密鍵のペアが含まれます。ドメイン全体の委任が有効になっている場合、クライアント ID はサービス アカウントの認証情報の一部でもあります。

アプリケーションが Google App Engine で実行されている場合、プロジェクトの作成時にサービス アカウントが自動的に設定されます。

アプリケーションが Google Compute Engine で実行されている場合、プロジェクトの作成時にサービス アカウントも自動的に設定されますが、Google Compute Engine インスタンスを作成するときに、アプリケーションがアクセスする必要があるスコープを指定する必要があります。詳細については、サービス アカウントを使用するようにインスタンスを準備するをご覧ください。

アプリケーションが Google App Engine または Google Compute Engine で実行されていない場合は、 でこれらの認証情報を取得する必要があります。サービス アカウントの認証情報を生成するか、すでに生成した公開認証情報を表示するには、次の操作を行います。

いつでも [ ] に戻って、メールアドレス、公開鍵のフィンガープリントなどの情報を表示したり、公開鍵と秘密鍵のペアを追加で生成したりできます。 のサービス アカウント認証情報の詳細については、 ヘルプファイルのサービス アカウントをご覧ください。

サービス アカウントのメールアドレスをメモし、サービス アカウントの秘密鍵ファイルをアプリケーションにアクセス可能な場所に保存します。アプリケーションが承認済みの API 呼び出しを行うには、これらの権限が必要です。

サービス アカウントにドメイン全体の権限を委任する

Google Workspace アカウントを使用すると、組織の Workspace 管理者は、Google Workspace ドメイン内のユーザーに代わって Workspace ユーザーデータにアクセスするアプリケーションを認可できます。たとえば、Google Calendar API を使用して Google Workspace ドメイン内のすべてのユーザーのカレンダーに予定を追加するアプリケーションの場合、ユーザーの代わりにサービス アカウントを使用して Google Calendar API にアクセスします。ドメイン内のユーザーに代わってデータにアクセスすることをサービス アカウントに承認することは、サービス アカウントへの「ドメイン全体の権限の委任」と呼ばれることもあります。

サービス アカウントにドメイン全体の権限を委任するには、Google Workspace ドメインの特権管理者が次の手順を完了する必要があります。

  1. Google Workspace ドメインの 管理コンソールで、メインメニュー > [セキュリティ] > [アクセスとデータ管理] > [API の制御] に移動します。
  2. [ドメイン全体の委任] ペインで、[ドメイン全体の委任を管理] を選択します。
  3. [新しく追加] をクリックします。
  4. [クライアント ID] フィールドに、サービス アカウントのクライアント ID を入力します。サービス アカウントのクライアント ID は で確認できます。
  5. [OAuth スコープ(カンマ区切り)] フィールドに、アプリケーションにアクセス権を付与するスコープのリストを入力します。たとえば、アプリケーションが Google Drive API と Google Calendar API にドメイン全体でフルアクセスする必要がある場合は、次のように入力します。https://www.googleapis.com/auth/drive, https://www.googleapis.com/auth/calendar
  6. [承認] をクリックします。

これで、アプリケーションは Workspace ドメイン内のユーザーとして API 呼び出しを行う権限(ユーザーの「権限借用」)を取得しました。これらの委任された API 呼び出しを行う準備をするときに、権限借用するユーザーを明示的に指定します。

委任された API 呼び出しの準備

Java

からクライアントのメールアドレスと秘密鍵を取得したら、Java 向けの Google API クライアント ライブラリを使用して、サービス アカウントの認証情報と、アプリケーションがアクセスする必要があるスコープから GoogleCredential オブジェクトを作成します。次に例を示します。

import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.services.sqladmin.SQLAdminScopes;

// ...

GoogleCredential credential = GoogleCredential.fromStream(new FileInputStream("MyProject-1234.json"))
    .createScoped(Collections.singleton(SQLAdminScopes.SQLSERVICE_ADMIN));

Google Cloud Platform でアプリを開発している場合は、代わりにアプリケーションのデフォルト認証情報を使用すると、プロセスを簡素化できます。

ドメイン全体の権限を委任する

サービス アカウントにドメイン全体のアクセス権を委任していて、ユーザー アカウントの権限を借用する場合は、GoogleCredential オブジェクトの createDelegated メソッドを使用して、ユーザー アカウントのメールアドレスを指定します。例:

GoogleCredential credential = GoogleCredential.fromStream(new FileInputStream("MyProject-1234.json"))
    .createScoped(Collections.singleton(SQLAdminScopes.SQLSERVICE_ADMIN))
    .createDelegated("workspace-user@example.com");

上記のコードでは、GoogleCredential オブジェクトを使用して createDelegated() メソッドを呼び出しています。createDelegated() メソッドの引数は、Workspace アカウントに属するユーザーである必要があります。リクエストを行うコードは、この認証情報を使用して、サービス アカウントを使用して Google API を呼び出します。

Python

からクライアントのメールアドレスと秘密鍵を取得したら、Python 用 Google API クライアント ライブラリを使用して次の手順を完了します。

  1. サービス アカウントの認証情報と、アプリケーションがアクセスする必要があるスコープから Credentials オブジェクトを作成します。次に例を示します。
    from google.oauth2 import service_account
    
    SCOPES = ['https://www.googleapis.com/auth/sqlservice.admin']
    SERVICE_ACCOUNT_FILE = '/path/to/service.json'
    
    credentials = service_account.Credentials.from_service_account_file(
            SERVICE_ACCOUNT_FILE, scopes=SCOPES)

    Google Cloud Platform でアプリを開発している場合は、代わりにアプリケーションのデフォルト認証情報を使用すると、プロセスを簡素化できます。

  2. ドメイン全体の権限を委任する

    サービス アカウントにドメイン全体のアクセス権を委任していて、ユーザー アカウントの権限を借用する場合は、既存の ServiceAccountCredentials オブジェクトの with_subject メソッドを使用します。次に例を示します。

    delegated_credentials = credentials.with_subject('user@example.org')

認証情報オブジェクトを使用して、アプリケーションで Google API を呼び出します。

HTTP/REST

からクライアント ID と秘密鍵を取得したら、アプリケーションは次の手順を完了する必要があります。

  1. ヘッダー、クレームセット、署名を含む JSON Web Token(JWT)を作成します。
  2. Google OAuth 2.0 認証サーバーからアクセス トークンをリクエストします。
  3. 認可サーバーが返す JSON レスポンスを処理します。

以降のセクションでは、これらの手順を完了する方法について説明します。

レスポンスにアクセス トークンが含まれている場合は、そのアクセス トークンを使用して Google API を呼び出すことができます。(レスポンスにアクセス トークンが含まれていない場合は、JWT とトークン リクエストが正しく形成されていないか、サービス アカウントにリクエストされたスコープへのアクセス権限がない可能性があります)。

アクセス トークンが期限切れになると、アプリケーションは別の JWT を生成して署名し、別のアクセス トークンをリクエストします。

サーバー アプリケーションは JWT を使用して Google 認可サーバーからトークンをリクエストし、そのトークンを使用して Google API エンドポイントを呼び出します。エンドユーザーは関与しません。

このセクションの残りの部分では、JWT の作成、JWT の署名、アクセス トークン リクエストの作成、レスポンスの処理について詳しく説明します。

JWT の作成

JWT は、ヘッダー、クレームセット、署名の 3 つの部分で構成されています。ヘッダーとクレームセットは JSON オブジェクトです。これらの JSON オブジェクトは UTF-8 バイトにシリアル化され、Base64url エンコードを使用してエンコードされます。このエンコードは、エンコード オペレーションの繰り返しによるエンコード変更に対する耐障害性を提供します。ヘッダー、クレームセット、署名はピリオド(.)文字で連結されます。

JWT は次のように構成されています。

{Base64url encoded header}.{Base64url encoded claim set}.{Base64url encoded signature}

署名のベース文字列は次のとおりです。

{Base64url encoded header}.{Base64url encoded claim set}
JWT ヘッダーの作成

ヘッダーは、署名アルゴリズム、アサーションの形式、JWT の署名に使用された [サービス アカウント キーのキー ID](https://cloud.google.com/iam/docs/reference/rest/v1/projects.serviceAccounts.keys) を示す 3 つのフィールドで構成されています。アルゴリズムと形式は必須で、各フィールドに設定できる値は 1 つだけです。追加のアルゴリズムと形式が導入されると、このヘッダーはそれに応じて変更されます。鍵 ID は省略可能です。間違った鍵 ID が指定されている場合、GCP はサービス アカウントに関連付けられているすべての鍵を試してトークンを検証し、有効な鍵が見つからなければトークンを拒否します。Google は、今後、キー ID が正しくないトークンを拒否する権利を有します。

サービス アカウントは、RSA SHA-256 アルゴリズムと JWT トークン形式に依存しています。結果として、ヘッダーの JSON 表現は次のようになります。

{"alg":"RS256","typ":"JWT", "kid":"370ab79b4513eb9bad7c9bd16a95cb76b5b2a56a"}

これを Base64url で表すと次のようになります。

          eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsICJraWQiOiIzNzBhYjc5YjQ1MTNlYjliYWQ3YzliZDE2YTk1Y2I3NmI1YjJhNTZhIn0=
JWT クレームセットの形成

JWT クレームセットには、リクエストされた権限(スコープ)、トークンのターゲット、発行元、トークンが発行された時刻、トークンの有効期間など、JWT に関する情報が含まれています。ほとんどのフィールドは必須です。JWT ヘッダーと同様に、JWT クレームセットは JSON オブジェクトであり、署名の計算に使用されます。

必須クレーム

JWT クレームセットで必要なクレームを以下に示します。クレームセット内の順序は任意です。

名前 説明
iss サービス アカウントのメールアドレス。
scope アプリケーションがリクエストする権限のスペース区切りリスト。
aud アサーションの対象となるデスクリプタ。アクセス トークン リクエストを行う場合、この値は常に https://oauth2.googleapis.com/token です。
exp アサーションの有効期限。1970 年 1 月 1 日 00:00:00 UTC からの経過秒数として指定します。この値は、発行時間から 1 時間以内にする必要があります。
iat アサーションが発行された時刻。1970 年 1 月 1 日 00:00:00 UTC からの経過秒数で指定します。

JWT クレームセットで必須のフィールドの JSON 表現は次のとおりです。

{
  "iss": "761326798069-r5mljlln1rd4lrbhg75efgigp36m78j5@developer.gserviceaccount.com",
  "scope": "https://www.googleapis.com/auth/devstorage.read_only",
  "aud": "https://oauth2.googleapis.com/token",
  "exp": 1328554385,
  "iat": 1328550785
}
その他の申し立て

エンタープライズ ケースによっては、アプリケーションがドメイン全体の委任を使用して、組織内の特定のユーザーに代わって動作できます。アプリケーションがユーザーの権限を借用するには、このタイプの権限借用を行う権限を付与する必要があります。通常、この権限は特権管理者が処理します。詳細については、API アクセスをドメイン全体の委任で制御するをご覧ください。

リソースへのアクセスを委任されたアプリケーションに付与するアクセス トークンを取得するには、JWT クレームにユーザーのメールアドレスを含め、sub フィールドの値として設定します。

名前 説明
sub アプリケーションがアクセス権の委任をリクエストするユーザーのメールアドレス。

アプリケーションにユーザーの権限借用権限がない場合、sub フィールドを含むアクセス トークン リクエストに対するレスポンスはエラーになります。

sub フィールドを含む JWT クレームセットの例を次に示します。

{
  "iss": "761326798069-r5mljlln1rd4lrbhg75efgigp36m78j5@developer.gserviceaccount.com",
  "sub": "some.user@example.com",
  "scope": "https://www.googleapis.com/auth/prediction",
  "aud": "https://oauth2.googleapis.com/token",
  "exp": 1328554385,
  "iat": 1328550785
}
JWT クレームセットのエンコード

JWT ヘッダーと同様に、JWT クレームセットは UTF-8 にシリアル化され、Base64url で安全にエンコードする必要があります。JWT クレームセットの JSON 表現の例を次に示します。

{
  "iss": "761326798069-r5mljlln1rd4lrbhg75efgigp36m78j5@developer.gserviceaccount.com",
  "scope": "https://www.googleapis.com/auth/prediction",
  "aud": "https://oauth2.googleapis.com/token",
  "exp": 1328554385,
  "iat": 1328550785
}
署名の計算

JSON Web Signature(JWS)は、JWT の署名生成のメカニズムをガイドする仕様です。署名の入力は、次のコンテンツのバイト配列です。

{Base64url encoded header}.{Base64url encoded claim set}

署名の計算には、JWT ヘッダーの署名アルゴリズムを使用する必要があります。Google OAuth 2.0 認可サーバーでサポートされている署名アルゴリズムは、SHA-256 ハッシュ アルゴリズムを使用する RSA のみです。これは、JWT ヘッダーの alg フィールドで RS256 として表現されます。

から取得した秘密鍵を使用して、SHA256withRSA(SHA-256 ハッシュ関数を使用した RSASSA-PKCS1-V1_5-SIGN)を使用して入力の UTF-8 表現に署名します。出力はバイト配列になります。

署名は Base64url でエンコードする必要があります。ヘッダー、クレームセット、署名はピリオド(.)文字で連結されます。結果は JWT です。次のようにします(わかりやすくするために改行を入れています)。

{Base64url encoded header}.
{Base64url encoded claim set}.
{Base64url encoded signature}

以下に、Base64url エンコード前の JWT の例を示します。

{"alg":"RS256","typ":"JWT"}.
{
"iss":"761326798069-r5mljlln1rd4lrbhg75efgigp36m78j5@developer.gserviceaccount.com",
"scope":"https://www.googleapis.com/auth/prediction",
"aud":"https://oauth2.googleapis.com/token",
"exp":1328554385,
"iat":1328550785
}.
[signature bytes]

以下は、署名され、送信可能な JWT の例です。

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI3NjEzMjY3OTgwNjktcjVtbGpsbG4xcmQ0bHJiaGc3NWVmZ2lncDM2bTc4ajVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLCJzY29wZSI6Imh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2F1dGgvcHJlZGljdGlvbiIsImF1ZCI6Imh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL29hdXRoMi92NC90b2tlbiIsImV4cCI6MTMyODU1NDM4NSwiaWF0IjoxMzI4NTUwNzg1fQ.UFUt59SUM2_AW4cRU8Y0BYVQsNTo4n7AFsNrqOpYiICDu37vVt-tw38UKzjmUKtcRsLLjrR3gFW3dNDMx_pL9DVjgVHDdYirtrCekUHOYoa1CMR66nxep5q5cBQ4y4u2kIgSvChCTc9pmLLNoIem-ruCecAJYgI9Ks7pTnW1gkOKs0x3YpiLpzplVHAkkHztaXiJdtpBcY1OXyo6jTQCa3Lk2Q3va1dPkh_d--GU2M5flgd8xNBPYw4vxyt0mP59XZlHMpztZt0soSgObf7G3GXArreF_6tpbFsS3z2t5zkEiHuWJXpzcYr5zWTRPDEHsejeBSG8EgpLDce2380ROQ

アクセス トークン リクエストの送信

署名付き JWT を生成すると、アプリケーションはそれをアクセス トークンのリクエストに使用できます。このアクセス トークン リクエストは HTTPS POST リクエストであり、本文は URL エンコードされています。URL は次のとおりです。

https://oauth2.googleapis.com/token

HTTPS POST リクエストでは、次のパラメータが必要です。

名前 説明
grant_type 必要に応じて URL エンコードされた次の文字列を使用します。 urn:ietf:params:oauth:grant-type:jwt-bearer
assertion JWT(署名を含む)。

アクセス トークン リクエストで使用される HTTPS POST リクエストの未加工ダンプは次のとおりです。

POST /token HTTP/1.1
Host: oauth2.googleapis.com
Content-Type: application/x-www-form-urlencoded

grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI3NjEzMjY3OTgwNjktcjVtbGpsbG4xcmQ0bHJiaGc3NWVmZ2lncDM2bTc4ajVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLCJzY29wZSI6Imh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2F1dGgvcHJlZGljdGlvbiIsImF1ZCI6Imh0dHBzOi8vYWNjb3VudHMuZ29vZ2xlLmNvbS9vL29hdXRoMi90b2tlbiIsImV4cCI6MTMyODU3MzM4MSwiaWF0IjoxMzI4NTY5NzgxfQ.ixOUGehweEVX_UKXv5BbbwVEdcz6AYS-6uQV6fGorGKrHf3LIJnyREw9evE-gs2bmMaQI5_UbabvI4k-mQE4kBqtmSpTzxYBL1TCd7Kv5nTZoUC1CmwmWCFqT9RE6D7XSgPUh_jF1qskLa2w0rxMSjwruNKbysgRNctZPln7cqQ

以下は、curl を使用した同じリクエストです。

curl -d 'grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI3NjEzMjY3OTgwNjktcjVtbGpsbG4xcmQ0bHJiaGc3NWVmZ2lncDM2bTc4ajVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLCJzY29wZSI6Imh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2F1dGgvcHJlZGljdGlvbiIsImF1ZCI6Imh0dHBzOi8vYWNjb3VudHMuZ29vZ2xlLmNvbS9vL29hdXRoMi90b2tlbiIsImV4cCI6MTMyODU3MzM4MSwiaWF0IjoxMzI4NTY5NzgxfQ.RZVpzWygMLuL-n3GwjW1_yhQhrqDacyvaXkuf8HcJl8EtXYjGjMaW5oiM5cgAaIorrqgYlp4DPF_GuncFqg9uDZrx7pMmCZ_yHfxhSCXru3gbXrZvAIicNQZMFxrEEn4REVuq7DjkTMyCMGCY1dpMa8aWfTQFt3Eh7smLchaZsU
' https://oauth2.googleapis.com/token

レスポンスへの対応

JWT とアクセス トークン リクエストが適切に形成され、サービス アカウントにオペレーションを実行する権限が付与されている場合、認可サーバーからの JSON レスポンスにはアクセス トークンが含まれます。レスポンスの例を次に示します。

{
  "access_token": "1/8xbJqaOZXSUZbHLl5EOtu1pxz3fmmetKx9W8CV4t79M",
  "scope": "https://www.googleapis.com/auth/prediction"
  "token_type": "Bearer",
  "expires_in": 3600
}

アクセス トークンは、expires_in 値で指定された期間中に再利用できます。

Google API の呼び出し

Java

GoogleCredential オブジェクトを使用して Google API を呼び出す手順は次のとおりです。

  1. GoogleCredential オブジェクトを使用して、呼び出す API のサービス オブジェクトを作成します。次に例を示します。
    SQLAdmin sqladmin =
        new SQLAdmin.Builder(httpTransport, JSON_FACTORY, credential).build();
  2. サービス オブジェクトによって提供されるインターフェースを使用して、API サービスにリクエストを送信します。たとえば、exciting-example-123 プロジェクト内の Cloud SQL データベースのインスタンスを一覧表示するには、次のコマンドを使用します。
    SQLAdmin.Instances.List instances =
        sqladmin.instances().list("exciting-example-123").execute();

Python

承認済みの Credentials オブジェクトを使用して Google API を呼び出すには、次の手順を行います。

  1. 呼び出す API のサービス オブジェクトを作成します。サービス オブジェクトを作成するには、API の名前とバージョン、承認済みの Credentials オブジェクトを指定して build 関数を呼び出します。たとえば、Cloud SQL Administration API のバージョン 1beta3 を呼び出すには、次のようにします。
    import googleapiclient.discovery
    
    sqladmin = googleapiclient.discovery.build('sqladmin', 'v1beta3', credentials=credentials)
  2. サービス オブジェクトによって提供されるインターフェースを使用して、API サービスにリクエストを送信します。たとえば、exciting-example-123 プロジェクト内の Cloud SQL データベースのインスタンスを一覧表示するには、次のコマンドを使用します。
    response = sqladmin.instances().list(project='exciting-example-123').execute()

HTTP/REST

アプリケーションがアクセス トークンを取得すると、API で必要なアクセス権が付与されている場合は、そのトークンを使用して、特定のサービス アカウントまたはユーザー アカウントに代わって Google API を呼び出すことができます。これを行うには、access_token クエリ パラメータまたは Authorization HTTP ヘッダー Bearer 値のいずれかを含めて、API へのリクエストにアクセス トークンを含めます。クエリ文字列はサーバーログに表示されるため、可能であれば HTTP ヘッダーを使用することをおすすめします。ほとんどの場合、クライアント ライブラリを使用して Google API への呼び出しを設定できます(Drive Files API を呼び出す場合など)。

すべての Google API を試して、そのスコープを確認するには、OAuth 2.0 Playground をご覧ください。

HTTP GET の例

Authorization: Bearer HTTP ヘッダーを使用して drive.files エンドポイント(Drive Files API)を呼び出す場合、呼び出しは次のようになります。独自のアクセス トークンを指定する必要があります。

GET /drive/v2/files HTTP/1.1
Host: www.googleapis.com
Authorization: Bearer access_token

以下は、access_token クエリ文字列パラメータを使用して、認証済みユーザーの同じ API を呼び出すコードです。

GET https://www.googleapis.com/drive/v2/files?access_token=access_token

curl の例

これらのコマンドは、curl コマンドライン アプリケーションでテストできます。HTTP ヘッダー オプションを使用する例を次に示します(推奨)。

curl -H "Authorization: Bearer access_token" https://www.googleapis.com/drive/v2/files

または、クエリ文字列パラメータ オプションを使用します。

curl https://www.googleapis.com/drive/v2/files?access_token=access_token

アクセス トークンの有効期限

Google OAuth 2.0 認証サーバーによって発行されたアクセス トークンは、expires_in 値で指定された期間が経過すると期限切れになります。アクセス トークンが期限切れになったら、アプリケーションは別の JWT を生成して署名し、別のアクセス トークンをリクエストする必要があります。

JWT エラーコード

error フィールド error_description フィールド 意味 解決方法
unauthorized_client Unauthorized client or scope in request. ドメイン全体の委任を使用しようとしている場合、サービス アカウントがユーザーのドメインの管理コンソールで承認されていません。

sub クレーム(フィールド)のユーザーに対して、管理コンソールの [ ドメイン全体の委任] ページでサービス アカウントが承認されていることを確認します。

通常は数分で完了しますが、Google アカウントのすべてのユーザーに承認が反映されるまでに最長で 24 時間ほどかかることがあります。

unauthorized_client Client is unauthorized to retrieve access tokens using this method, or client not authorized for any of the scopes requested. 管理コンソールで、クライアント ID(数値)ではなくクライアントのメールアドレスを使用してサービス アカウントが承認されています。 管理コンソールの [ ドメイン全体の委任] ページで、クライアントを削除し、数値 ID を使用して再度追加します。
access_denied (任意の値) ドメイン全体の委任を使用している場合、リクエストされたスコープの 1 つ以上が管理コンソールで承認されていません。

管理コンソールの [ ドメイン全体の委任] ページで、sub クレーム(フィールド)のユーザーに対してサービス アカウントが承認されていること、および JWT の scope クレームでリクエストしているすべてのスコープが含まれていることを確認します。

通常は数分で完了しますが、Google アカウントのすべてのユーザーに承認が反映されるまでに最長で 24 時間ほどかかることがあります。

admin_policy_enforced (任意の値) Google Workspace 管理者のポリシーにより、リクエストされた 1 つ以上のスコープを Google アカウントが承認できません。

OAuth クライアント ID にアクセス権が明示的に付与されるまで、管理者がすべてのスコープまたは機密性の高いスコープと制限付きスコープへのアクセスを制限する方法については、Google Workspace 管理者向けのヘルプ記事「Google Workspace のデータにアクセスできるサードパーティ製アプリと内部アプリを制御する」をご覧ください。

invalid_client (任意の値)

OAuth クライアントまたは JWT トークンが無効であるか、構成が正しくありません。

詳細については、エラーの説明を参照してください。

JWT トークンが有効で、正しいクレームが含まれていることを確認します。

OAuth クライアントとサービス アカウントが正しく構成され、正しいメールアドレスを使用していることを確認します。

JWT トークンが正しく、リクエストのクライアント ID に対して発行されていることを確認します。

invalid_grant Not a valid email. このユーザーは存在しません。 sub クレーム(フィールド)のメールアドレスが正しいことを確認します。
invalid_grant

Invalid JWT: Token must be a short-lived token (60 minutes) and in a reasonable timeframe. Check your 'iat' and 'exp' values and use a clock with skew to account for clock differences between systems.

通常、これはローカル システム時間が正しくないことを意味します。また、exp 値が iat 値から 65 分以上先である場合や、exp 値が iat 値より小さい場合にも発生する可能性があります。

JWT が生成されるシステムの時計が正しいことを確認します。必要に応じて、時刻を Google NTP と同期します。

invalid_grant Invalid JWT Signature.

JWT アサーションの署名に、クライアントのメールアドレスで識別されるサービス アカウントに関連付けられていない秘密鍵が使用されているか、使用された鍵が削除、無効化、または期限切れになっている。

また、JWT アサーションが正しくエンコードされていない可能性があります。JWT アサーションは、改行や等号の埋め込みなしで Base64 でエンコードする必要があります。

JWT クレームセットをデコードし、アサーションを署名した鍵がサービス アカウントに関連付けられていることを確認します。

Google 提供の OAuth ライブラリを使用して、JWT が正しく生成されていることを確認します。

invalid_scope Invalid OAuth scope or ID token audience provided. スコープがリクエストされていない(スコープのリストが空である)、またはリクエストされたスコープのいずれかが存在しない(つまり無効である)。

JWT の scope クレーム(フィールド)が入力されていることを確認します。また、そこに含まれるスコープを、使用する API のドキュメントに記載されているスコープと比較して、エラーや誤字脱字がないことを確認します。

scope クレームのスコープのリストは、カンマではなくスペースで区切る必要があります。

disabled_client The OAuth client was disabled. JWT アサーションの署名に使用される鍵が無効になっています。

に移動し、[IAM と管理] > [サービス アカウント] で、アサーションの署名に使用される「Key ID」を含むサービス アカウントを有効にします。

org_internal This client is restricted to users within its organization. リクエストの OAuth クライアント ID は、特定の Google Cloud 組織の Google アカウントへのアクセスを制限するプロジェクトの一部です。

組織のサービス アカウントを使用して認証します。OAuth アプリケーションのユーザータイプの構成を確認します。

付録: OAuth を使用しないサービス アカウントの認可

一部の Google API では、OAuth 2.0 アクセス トークンではなく署名付き JWT を署名なしトークンとして直接使用する認証済み API を呼び出すことができます。こちらが使用できる場合、API を呼び出す前に Google の認証サーバーへのネットワーク リクエストを作成する必要がなくなります。

呼び出す API に Google API GitHub リポジトリで公開されているサービス定義がある場合、アクセス トークンではなく JWT を使用して認証済み API 呼び出しを行うことができます。手順は次のとおりです。

  1. 上記の説明に従ってサービス アカウントを作成します。アカウントの作成時に取得した JSON ファイルは必ず保存してください。
  2. jwt.io にある標準の JWT ライブラリなどを使用して、次の例のようにヘッダーとペイロードを含む JWT を作成します。
    {
      "alg": "RS256",
      "typ": "JWT",
      "kid": "abcdef1234567890"
    }
    .
    {
      "iss": "123456-compute@developer.gserviceaccount.com",
      "sub": "123456-compute@developer.gserviceaccount.com",
      "aud": "https://firestore.googleapis.com/",
      "iat": 1511900000,
      "exp": 1511903600
    }
    • ヘッダーの kid フィールドに、サービス アカウントの秘密鍵 ID を指定します。この値は、サービス アカウント JSON ファイルの private_key_id フィールドで確認できます。
    • iss フィールドと sub フィールドに、サービス アカウントのメールアドレスを指定します。この値は、サービス アカウントの JSON ファイルの client_email フィールドで確認できます。
    • aud フィールドに API エンドポイントを指定します。例: https://SERVICE.googleapis.com/
    • iat フィールドには現在の Unix 時間を指定し、exp フィールドには JWT の有効期限が切れる 3,600 秒後の時間を指定します。

サービス アカウントの JSON ファイルにある秘密鍵を使用して、RSA-256 で JWT に署名します。

次に例を示します。

Java

google-api-java-clientjava-jwt を使用した実装例:

GoogleCredential credential =
        GoogleCredential.fromStream(new FileInputStream("MyProject-1234.json"));
PrivateKey privateKey = credential.getServiceAccountPrivateKey();
String privateKeyId = credential.getServiceAccountPrivateKeyId();

long now = System.currentTimeMillis();

try {
    Algorithm algorithm = Algorithm.RSA256(null, privateKey);
    String signedJwt = JWT.create()
        .withKeyId(privateKeyId)
        .withIssuer("123456-compute@developer.gserviceaccount.com")
        .withSubject("123456-compute@developer.gserviceaccount.com")
        .withAudience("https://firestore.googleapis.com/")
        .withIssuedAt(new Date(now))
        .withExpiresAt(new Date(now + 3600 * 1000L))
        .sign(algorithm);
} catch ...

Python

PyJWT を使用した実装例:

iat = time.time()
exp = iat + 3600
payload = {'iss': '123456-compute@developer.gserviceaccount.com',
           'sub': '123456-compute@developer.gserviceaccount.com',
           'aud': 'https://firestore.googleapis.com/',
           'iat': iat,
           'exp': exp}
additional_headers = {'kid': PRIVATE_KEY_ID_FROM_JSON}
signed_jwt = jwt.encode(payload, PRIVATE_KEY_FROM_JSON, headers=additional_headers,
                       algorithm='RS256')
  1. 署名付き JWT をベアラートークンとして使用して API を呼び出します。
    GET /v1/projects/abc/databases/123/indexes HTTP/1.1
    Authorization: Bearer SIGNED_JWT
    Host: firestore.googleapis.com

クロスアカウント保護機能の実装

ユーザーのアカウントを保護するために、Google のクロスアカウント保護サービスを利用したクロスアカウント保護を実装することもおすすめします。このサービスを使用すると、セキュリティ イベント通知を定期購読して、ユーザー アカウントの大きな変更に関する情報をアプリに提供できます。その後、イベントへの対応方法に応じて、この情報に基づいてアクションを実行できます。

Google のクロスアカウント保護サービスからアプリに送信されるイベントの種類には、次のものがあります。

  • https://schemas.openid.net/secevent/risc/event-type/sessions-revoked
  • https://schemas.openid.net/secevent/oauth/event-type/token-revoked
  • https://schemas.openid.net/secevent/risc/event-type/account-disabled

クロスアカウント保護の実装方法と利用可能なイベントの一覧については、 クロスアカウント保護でユーザー アカウントを保護する をご覧ください。