クライアントサイド ウェブ アプリケーション用の OAuth 2.0

このドキュメントでは、JavaScript ウェブ アプリケーションから Google API にアクセスするための OAuth 2.0 認可を実装する方法について説明します。OAuth 2.0 では、ユーザー名やパスワードなどの情報を秘密にしたまま、ユーザーが特定のデータをアプリケーションと共有できます。たとえば、アプリケーションは OAuth 2.0 を使用して、Google ドライブにファイルを保存する権限をユーザーから取得できます。

この OAuth 2.0 フローは、暗黙的権限付与フローと呼ばれます。これは、ユーザーがアプリケーションにアクセスしている間のみ API にアクセスするアプリケーション向けに設計されています。これらのアプリケーションは機密情報を保存できません。

このフローでは、アプリはクエリ パラメータを使用してアプリとアプリが必要とする API アクセスの種類を識別する Google URL を開きます。URL は、現在のブラウザ ウィンドウまたはダイアログで開くことができます。ユーザーは Google で認証を行い、リクエストされた権限を付与できます。 その後、Google はユーザーをアプリにリダイレクトします。リダイレクトにはアクセス トークンが含まれており、アプリはこれを検証して API リクエストの作成に使用します。

Google API クライアント ライブラリと Google Identity Services

JavaScript 用 Google API クライアント ライブラリを使用して Google への承認済み呼び出しを行う場合は、Google Identity Services JavaScript ライブラリを使用して承認フローを処理する必要があります。PKCE を使用したより安全な認可コードフローに基づく Google Identity Services のコードモデルを使用することを強くおすすめします。

前提条件

プロジェクトでAPI を有効にする

Google API を呼び出すアプリケーションは、API Console でそれらの API を有効にする必要があります。

プロジェクトで API を有効にするには:

  1. Google API Console で API ライブラリを開きます。
  2. プロンプトが表示されたら、プロジェクトのいずれかを選択するか、新しいプロジェクトを作成します。
  3. API ライブラリには、利用可能なすべての API がプロダクト ファミリーと人気度に応じてグループ化されて表示されます。有効にする API がリストで見当たらない場合は、検索して見つけるか、その API が属するプロダクト ファミリーの [すべて表示] をクリックします。
  4. 有効にする API を選択し、[有効にする] ボタンをクリックします。
  5. プロンプトが表示されたら、請求機能を有効にします。
  6. プロンプトが表示されたら、API の利用規約を確認し、同意します。

承認認証情報を作成する

OAuth 2.0 を使用して Google API にアクセスするアプリケーションには、Google の OAuth 2.0 サーバーにアプリケーションを識別させる認証情報が必要です。プロジェクトの認証情報を作成する手順は次のとおりです。アプリケーションは、認証情報を使用して、そのプロジェクトで有効にした API にアクセスできます。

  1. [クライアント] ページに移動します。
  2. [Create Client] をクリックします。
  3. アプリケーションの種類として [ウェブ アプリケーション] を選択します。
  4. フォームに入力します。JavaScript を使用して承認済みの Google API リクエストを行うアプリケーションでは、承認済みの JavaScript 生成元を指定する必要があります。オリジンは、アプリケーションが OAuth 2.0 サーバーにリクエストを送信できるドメインを識別します。これらのオリジンは、Google の検証ルールに準拠している必要があります。

アクセス スコープを特定する

スコープを使用すると、アプリケーションは必要なリソースへのアクセスのみをリクエストできます。また、ユーザーはアプリケーションに付与するアクセス量を制御できます。したがって、リクエストするスコープの数とユーザーの同意を得られる可能性は、反比例の関係にある可能性があります。

OAuth 2.0 認可の実装を開始する前に、アプリがアクセス許可を必要とするスコープを特定することをおすすめします。

OAuth 2.0 API スコープのドキュメントには、Google API へのアクセスに使用できるスコープの完全なリストが記載されています。

OAuth 2.0 アクセス トークンの取得

次の手順は、アプリケーションが Google の OAuth 2.0 サーバーとやり取りして、ユーザーの代わりに API リクエストを実行することに対するユーザーの同意を得る方法を示しています。ユーザーの承認が必要な Google API リクエストを実行するには、アプリがその同意を得ている必要があります。

ステップ 1: Google の OAuth 2.0 サーバーにリダイレクトする

ユーザーのデータにアクセスする権限をリクエストするには、ユーザーを Google の OAuth 2.0 サーバーにリダイレクトします。

OAuth 2.0 エンドポイント

https://accounts.google.com/o/oauth2/v2/auth で Google の OAuth 2.0 エンドポイントからアクセスをリクエストする URL を生成します。このエンドポイントには HTTPS 経由でアクセスできます。プレーンな HTTP 接続は拒否されます。

Google 認可サーバーは、ウェブサーバー アプリケーションに対して次のクエリ文字列パラメータをサポートしています。

パラメータ
client_id 必須

アプリケーションのクライアント ID。この値は、Cloud コンソールの [クライアント] ページで確認できます。

redirect_uri 必須

ユーザーが認可フローを完了した後に、API サーバーがユーザーをリダイレクトする場所を指定します。この値は、クライアントの Cloud コンソールのクライアント ページで構成した OAuth 2.0 クライアントの承認済みリダイレクト URI のいずれかと完全に一致する必要があります。この値が、指定された client_id の承認済みリダイレクト URI と一致しない場合、redirect_uri_mismatch エラーが発生します。

http または https スキーム、大文字と小文字の区別、末尾のスラッシュ(「/」)はすべて一致する必要があります。

response_type 必須

JavaScript アプリケーションでは、パラメータの値を token に設定する必要があります。 この値は、Google 認可サーバーに対して、ユーザーが認可プロセスを完了した後にリダイレクトされる URI(#)のフラグメント識別子で、アクセス トークンを name=value ペアとして返すように指示します。

scope 必須

アプリケーションがユーザーに代わってアクセスできるリソースを識別するスコープのスペース区切りのリスト。これらの値は、Google がユーザーに表示する同意画面に反映されます。

スコープを使用すると、アプリケーションは必要なリソースへのアクセスのみをリクエストできます。また、ユーザーはアプリケーションに付与するアクセス量を制御できます。したがって、リクエストするスコープの数とユーザーの同意を得られる可能性は反比例の関係にあります。

アプリケーションでは、可能な限りコンテキスト内の認可スコープへのアクセスをリクエストすることをおすすめします。段階的な認可を使用して、状況に合わせてユーザーデータへのアクセスをリクエストすることで、アプリケーションがリクエストしているアクセスを必要とする理由をユーザーが理解しやすくなります。

state 推奨

アプリケーションが認可リクエストと認可サーバーのレスポンスの間で状態を維持するために使用する任意の文字列値を指定します。サーバーは、ユーザーがアプリケーションのアクセス リクエストを承認または拒否した後、redirect_uri の URL フラグメント識別子(#)で name=value ペアとして送信した正確な値を返します。

このパラメータは、ユーザーをアプリケーション内の正しいリソースに誘導する、ノンスを送信する、クロスサイト リクエスト フォージェリを軽減するなど、いくつかの目的で使用できます。redirect_uri は推測される可能性があるため、state 値を使用すると、受信接続が認証リクエストの結果であるという確実性が高まります。ランダムな文字列を生成するか、Cookie のハッシュまたはクライアントの状態をキャプチャする別の値をエンコードすると、レスポンスを検証して、リクエストとレスポンスが同じブラウザで発生したことを確認し、クロスサイト リクエスト フォージェリなどの攻撃から保護できます。state トークンを作成して確認する方法の例については、OpenID Connect のドキュメントをご覧ください。

include_granted_scopes 省略可

アプリは、増分認証を使用して、コンテキスト内の追加スコープへのアクセスをリクエストできます。このパラメータの値を true に設定し、認可リクエストが承認されると、新しいアクセス トークンは、ユーザーが以前にアプリケーション アクセスを付与したスコープもカバーします。例については、増分承認のセクションをご覧ください。

login_hint 省略可

アプリケーションが認証を試みるユーザーを把握している場合は、このパラメータを使用して Google 認証サーバーにヒントを提供できます。サーバーは、ログイン フォームのメール フィールドに事前入力するか、適切なマルチログイン セッションを選択することで、ログインフローを簡素化するためにヒントを使用します。

パラメータ値を、ユーザーの Google ID に相当するメールアドレスまたは sub 識別子に設定します。

prompt 省略可

ユーザーに表示するプロンプトのスペース区切りのリスト(大文字と小文字を区別)。このパラメータを指定しない場合、ユーザーはプロジェクトがアクセスをリクエストした初回のみプロンプトが表示されます。詳しくは、 再同意を求めるをご覧ください。

次の値があります。

none 認証画面や同意画面を表示しないでください。他の値と一緒に指定することはできません。
consent ユーザーに同意を求める。
select_account ユーザーにアカウントの選択を求める。

Google の認可サーバーへのリダイレクトの例

以下に URL の例を示します。読みやすくするために改行とスペースを入れています。

https://accounts.google.com/o/oauth2/v2/auth?
 scope=https%3A//www.googleapis.com/auth/drive.metadata.readonly%20https%3A//www.googleapis.com/auth/calendar.readonly&
 include_granted_scopes=true&
 response_type=token&
 state=state_parameter_passthrough_value&
 redirect_uri=https%3A//developers.google.com/oauthplayground&
 client_id=client_id

リクエスト URL を作成したら、ユーザーをその URL にリダイレクトします。

JavaScript サンプルコード

次の JavaScript スニペットは、Google API クライアント ライブラリ for JavaScript を使用せずに JavaScript で認可フローを開始する方法を示しています。この OAuth 2.0 エンドポイントはクロスオリジン リソース シェアリング(CORS)をサポートしていないため、このスニペットは、そのエンドポイントへのリクエストを開くフォームを作成します。

/*
 * Create form to request access token from Google's OAuth 2.0 server.
 */
function oauthSignIn() {
  // Google's OAuth 2.0 endpoint for requesting an access token
  var oauth2Endpoint = 'https://accounts.google.com/o/oauth2/v2/auth';

  // Create <form> element to submit parameters to OAuth 2.0 endpoint.
  var form = document.createElement('form');
  form.setAttribute('method', 'GET'); // Send as a GET request.
  form.setAttribute('action', oauth2Endpoint);

  // Parameters to pass to OAuth 2.0 endpoint.
  var params = {'client_id': 'YOUR_CLIENT_ID',
                'redirect_uri': 'YOUR_REDIRECT_URI',
                'response_type': 'token',
                'scope': 'https://www.googleapis.com/auth/drive.metadata.readonly https://www.googleapis.com/auth/calendar.readonly',
                'include_granted_scopes': 'true',
                'state': 'pass-through value'};

  // Add form parameters as hidden input values.
  for (var p in params) {
    var input = document.createElement('input');
    input.setAttribute('type', 'hidden');
    input.setAttribute('name', p);
    input.setAttribute('value', params[p]);
    form.appendChild(input);
  }

  // Add form to page and submit it to open the OAuth 2.0 endpoint.
  document.body.appendChild(form);
  form.submit();
}

ステップ 2: Google がユーザーに同意を求める

このステップでは、ユーザーがアプリケーションにリクエストされたアクセス権を付与するかどうかを決定します。この段階で、Google は同意ウィンドウを表示します。このウィンドウには、アプリケーションの名前、ユーザーの認証情報を使用してアクセス許可をリクエストしている Google API サービス、付与されるアクセス スコープの概要が表示されます。ユーザーは、アプリケーションからリクエストされた 1 つ以上のスコープへのアクセス権の付与に同意するか、リクエストを拒否できます。

この段階では、アプリケーションは何もする必要はありません。Google の OAuth 2.0 サーバーからアクセスが許可されたかどうかを示すレスポンスが返されるのを待ちます。このレスポンスについては、次のステップで説明します。

エラー

Google の OAuth 2.0 認可エンドポイントへのリクエストで、想定される認証フローと認可フローではなく、ユーザー向けのエラー メッセージが表示されることがあります。一般的なエラーコードと推奨される解決策は次のとおりです。

admin_policy_enforced

Google Workspace 管理者のポリシーにより、Google アカウントはリクエストされた 1 つ以上のスコープを承認できません。管理者が OAuth クライアント ID にアクセスを明示的に付与するまで、すべてのスコープ、または機密スコープと制限付きスコープへのアクセスを制限する方法については、Google Workspace 管理者向けのヘルプ記事 Google Workspace のデータにアクセスできるサードパーティ製アプリと内部アプリを制御するをご覧ください。

disallowed_useragent

認可エンドポイントが、Google の OAuth 2.0 ポリシーで許可されていない埋め込みユーザー エージェント内に表示されている。

iOS と macOS のデベロッパーは、WKWebView で承認リクエストを開くときに、このエラーが発生することがあります。デベロッパーは、代わりに Google ログイン for iOS や OpenID Foundation の AppAuth for iOS などの iOS ライブラリを使用する必要があります。

ウェブ デベロッパーは、iOS または macOS アプリが埋め込みユーザー エージェントで一般的なウェブリンクを開き、ユーザーがサイトから Google の OAuth 2.0 認可エンドポイントに移動したときに、このエラーに遭遇することがあります。デベロッパーは、ユニバーサル リンク ハンドラとデフォルトのブラウザアプリの両方を含む、オペレーティング システムのデフォルトのリンクハンドラで一般的なリンクを開けるようにする必要があります。SFSafariViewController ライブラリもサポートされているオプションです。

org_internal

リクエスト内の OAuth クライアント ID は、特定の Google Cloud 組織内の Google アカウントへのアクセスを制限するプロジェクトの一部です。この構成オプションの詳細については、OAuth 同意画面の設定に関するヘルプ記事のユーザータイプのセクションをご覧ください。

invalid_client

リクエスト元のオリジンが、このクライアントに対して承認されていません。origin_mismatch をご覧ください。

deleted_client

リクエストに使用されている OAuth クライアントが削除されています。削除は、手動で行うことも、未使用のクライアント の場合は自動的に行うこともできます。削除したクライアントは、削除後 30 日以内であれば復元できます。 詳細 。

invalid_grant

増分認証を使用している場合、トークンの有効期限が切れているか、無効になっている可能性があります。 ユーザーを再度認証し、新しいトークンを取得するためのユーザーの同意を求めます。このエラーが引き続き表示される場合は、アプリケーションが正しく構成されていること、リクエストで正しいトークンとパラメータを使用していることを確認してください。それ以外の場合は、ユーザー アカウントが削除または無効になっている可能性があります。

origin_mismatch

認可リクエストを送信した JavaScript のスキーム、ドメイン、ポートのいずれかが、OAuth クライアント ID に登録されている承認済みの JavaScript 生成元 URI と一致していない可能性があります。Google Cloud コンソールのクライアント ページで、承認済みの JavaScript 生成元を確認します。

redirect_uri_mismatch

認可リクエストで渡された redirect_uri が、OAuth クライアント ID の承認済みリダイレクト URI と一致しません。Google Cloud コンソールの [クライアント] ページで、承認済みのリダイレクト URI を確認します。

認可リクエストを送信した JavaScript のスキーム、ドメイン、ポートのいずれかが、OAuth クライアント ID に登録されている承認済みの JavaScript 生成元 URI と一致していない可能性があります。Google Cloud コンソールのクライアント ページで、承認済みの JavaScript 生成元を確認します。

redirect_uri パラメータは、非推奨となり、サポートが終了した OAuth 帯域外(OOB)フローを参照している可能性があります。統合を更新するには、移行ガイドをご覧ください。

invalid_request

リクエストに問題がありました。これには、次のようなさまざまな理由が考えられます。

  • リクエストの形式が正しくありませんでした
  • リクエストに必須パラメータが含まれていませんでした
  • リクエストで、Google がサポートしていない認証方法が使用されています。OAuth 統合で推奨される統合方法が使用されていることを確認する

ステップ 3: OAuth 2.0 サーバーのレスポンスを処理する

OAuth 2.0 エンドポイント

OAuth 2.0 サーバーは、アクセス トークン リクエストで指定された redirect_uri にレスポンスを送信します。

ユーザーがリクエストを承認すると、レスポンスにアクセス トークンが格納されます。ユーザーがリクエストを承認しないと、レスポンスにエラー メッセージが格納されます。アクセス トークンまたはエラー メッセージは、次の例に示すように、リダイレクト URI のハッシュ フラグメントで返されます。

  • アクセス トークンのレスポンス:

    https://oauth2.example.com/callback#access_token=4/P7q7W91&token_type=Bearer&expires_in=3600

    access_token パラメータに加えて、フラグメント文字列には、常に Bearer に設定される token_type パラメータと、トークンの有効期間を秒単位で指定する expires_in パラメータも含まれます。アクセストークン要求でstateパラメータが指定されていた場合、その値も応答に含まれます。

  • エラー レスポンス:
    https://oauth2.example.com/callback#error=access_denied

OAuth 2.0 サーバーのレスポンスの例

このフローをテストするには、次のサンプル URL をクリックします。この URL は、Google ドライブ内のファイルのメタデータを表示するための読み取り専用アクセスと、Google カレンダーの予定を表示するための読み取り専用アクセスをリクエストします。

https://accounts.google.com/o/oauth2/v2/auth?
 scope=https%3A//www.googleapis.com/auth/drive.metadata.readonly%20https%3A//www.googleapis.com/auth/calendar.readonly&
 include_granted_scopes=true&
 response_type=token&
 state=state_parameter_passthrough_value&
 redirect_uri=https%3A//developers.google.com/oauthplayground&
 client_id=client_id

OAuth 2.0 フローが完了すると、ブラウザは OAuth フローをテストするためのツールである OAuth 2.0 Playground にリダイレクトされます。OAuth 2.0 Playground が認証コードを自動的に取得したことがわかります。

ステップ 4: ユーザーが付与したスコープを確認する

複数の権限(スコープ)をリクエストした場合、ユーザーがアプリにすべての権限へのアクセスを許可しないことがあります。アプリは、実際に付与されたスコープを確認し、一部の権限が拒否された状況を適切に処理する必要があります。通常は、拒否されたスコープに依存する機能を無効にします。

例外もございます。ドメイン全体の権限の委任が設定されている Google Workspace Enterprise アプリ、または信頼できるとしてマークされているアプリでは、詳細な権限の同意画面が省略されます。これらのアプリでは、詳細な権限の同意画面は表示されません。代わりに、アプリはリクエストされたすべてのスコープを受け取るか、まったく受け取らないかのいずれかになります。

詳細については、詳細な権限を処理する方法をご覧ください。

OAuth 2.0 エンドポイント

ユーザーが特定のスコープへのアクセスをアプリケーションに許可しているかどうかを確認するには、アクセス トークン レスポンスの scope フィールドを調べます。アクセス トークンによって付与されるアクセス スコープ。スペース区切りの大文字と小文字を区別する文字列のリストとして表されます。

たとえば、次のアクセス トークン レスポンスの例は、ユーザーがアプリケーションに読み取り専用のドライブ アクティビティとカレンダーの予定の権限へのアクセスを許可したことを示しています。

  {
    "access_token": "1/fFAGRNJru1FTz70BzhT3Zg",
    "expires_in": 3920,
    "token_type": "Bearer",
    "scope": "https://www.googleapis.com/auth/drive.metadata.readonly https://www.googleapis.com/auth/calendar.readonly",
    "refresh_token": "1//xEoDL4iW3cxlI7yDbSRFYNG01kVKM2C-259HOF2aQbI"
  }

Google API の呼び出し

OAuth 2.0 エンドポイント

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

OAuth 2.0 Playground で、すべての Google API を試して、そのスコープを確認できます。

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

JavaScript サンプルコード

以下のコード スニペットは、CORS(クロスオリジン リソース シェアリング)を使用して Google API にリクエストを送信する方法を示しています。この例では、JavaScript 用の Google API クライアント ライブラリを使用していません。ただし、クライアント ライブラリを使用していない場合でも、そのライブラリのドキュメントにある CORS サポート ガイドは、これらのリクエストをより深く理解するのに役立ちます。

このコード スニペットでは、access_token 変数は、承認されたユーザーに代わって API リクエストを行うために取得したトークンを表します。完全な例では、そのトークンをブラウザのローカル ストレージに保存し、API リクエストを行うときに取得する方法を示しています。

var xhr = new XMLHttpRequest();
xhr.open('GET',
    'https://www.googleapis.com/drive/v3/about?fields=user&' +
    'access_token=' + params['access_token']);
xhr.onreadystatechange = function (e) {
  console.log(xhr.response);
};
xhr.send(null);

サンプルコードの全文

OAuth 2.0 エンドポイント

このコードサンプルは、JavaScript 用 Google API クライアント ライブラリを使用せずに JavaScript で OAuth 2.0 フローを完了する方法を示しています。このコードは、API リクエストを試すボタンを表示する HTML ページ用です。ボタンをクリックすると、ページがブラウザのローカル ストレージに API アクセス トークンを保存しているかどうかがコードによって確認されます。一致する場合は、API リクエストを実行します。それ以外の場合は、OAuth 2.0 フローを開始します。

OAuth 2.0 フローの場合、ページは次の手順に沿って処理を行います。

  1. ユーザーを Google の OAuth 2.0 サーバーにリダイレクトします。このサーバーは、https://www.googleapis.com/auth/drive.metadata.readonly スコープと https://www.googleapis.com/auth/calendar.readonly スコープへのアクセスをリクエストします。
  2. リクエストされた 1 つ以上のスコープへのアクセスを許可(または拒否)すると、ユーザーは元のページにリダイレクトされます。このページは、フラグメント識別子文字列からアクセス トークンを解析します。
  3. このページでは、ユーザーがアプリケーションにアクセスを許可したスコープを確認します。
  4. ユーザーがリクエストされたスコープへのアクセスを許可している場合、ページはアクセス トークンを使用してサンプル API リクエストを行います。

    API リクエストは、Drive API の about.get メソッドを呼び出して、承認されたユーザーの Google ドライブ アカウントに関する情報を取得します。

  5. リクエストが正常に実行されると、API レスポンスがブラウザのデバッグ コンソールに記録されます。

アプリへのアクセス権は、Google アカウントの [権限] ページで取り消すことができます。アプリは、クライアント ID の作成時に OAuth 同意画面のブランディング ページで指定したアプリケーション名として表示されます。

このコードをローカルで実行するには、認証情報に対応する YOUR_CLIENT_ID 変数と YOUR_REDIRECT_URI 変数の値を設定する必要があります。YOUR_REDIRECT_URI 変数は、ページが提供されている URL と同じ URL に設定する必要があります。この値は、Cloud Console の [クライアント] ページで構成した OAuth 2.0 クライアントの承認済みリダイレクト URI のいずれかと完全に一致する必要があります。この値が承認済みの URI と一致しない場合、redirect_uri_mismatch エラーが発生します。また、プロジェクトでこのリクエストに適切な API を有効にする必要があります。

<html><head></head><body>
<script>
  var YOUR_CLIENT_ID = 'REPLACE_THIS_VALUE';
  var YOUR_REDIRECT_URI = 'REPLACE_THIS_VALUE';

  // Parse query string to see if page request is coming from OAuth 2.0 server.
  var fragmentString = location.hash.substring(1);
  var params = {};
  var regex = /([^&=]+)=([^&]*)/g, m;
  while (m = regex.exec(fragmentString)) {
    params[decodeURIComponent(m[1])] = decodeURIComponent(m[2]);
  }
  if (Object.keys(params).length > 0 && params['state']) {
    if (params['state'] == localStorage.getItem('state')) {
      localStorage.setItem('oauth2-test-params', JSON.stringify(params) );

      trySampleRequest();
    } else {
      console.log('State mismatch. Possible CSRF attack');
    }
  }

  // Function to generate a random state value
  function generateCryptoRandomState() {
    const randomValues = new Uint32Array(2);
    window.crypto.getRandomValues(randomValues);

    // Encode as UTF-8
    const utf8Encoder = new TextEncoder();
    const utf8Array = utf8Encoder.encode(
      String.fromCharCode.apply(null, randomValues)
    );

    // Base64 encode the UTF-8 data
    return btoa(String.fromCharCode.apply(null, utf8Array))
      .replace(/\+/g, '-')
      .replace(/\//g, '_')
      .replace(/=+$/, '');
  }

  // If there's an access token, try an API request.
  // Otherwise, start OAuth 2.0 flow.
  function trySampleRequest() {
    var params = JSON.parse(localStorage.getItem('oauth2-test-params'));
    if (params && params['access_token']) { 
      // User authorized the request. Now, check which scopes were granted.
      if (params['scope'].includes('https://www.googleapis.com/auth/drive.metadata.readonly')) {
        // User authorized read-only Drive activity permission.
        // Calling the APIs, etc.
        var xhr = new XMLHttpRequest();
        xhr.open('GET',
          'https://www.googleapis.com/drive/v3/about?fields=user&' +
          'access_token=' + params['access_token']);
        xhr.onreadystatechange = function (e) {
          if (xhr.readyState === 4 && xhr.status === 200) {
            console.log(xhr.response);
          } else if (xhr.readyState === 4 && xhr.status === 401) {
            // Token invalid, so prompt for user permission.
            oauth2SignIn();
          }
        };
        xhr.send(null);
      }
      else {
        // User didn't authorize read-only Drive activity permission.
        // Update UX and application accordingly
        console.log('User did not authorize read-only Drive activity permission.');
      }

      // Check if user authorized Calendar read permission.
      if (params['scope'].includes('https://www.googleapis.com/auth/calendar.readonly')) {
        // User authorized Calendar read permission.
        // Calling the APIs, etc.
        console.log('User authorized Calendar read permission.');
      }
      else {
        // User didn't authorize Calendar read permission.
        // Update UX and application accordingly
        console.log('User did not authorize Calendar read permission.');
      } 
    } else {
      oauth2SignIn();
    }
  }

  /*
   * Create form to request access token from Google's OAuth 2.0 server.
   */
  function oauth2SignIn() {
    // create random state value and store in local storage
    var state = generateCryptoRandomState();
    localStorage.setItem('state', state);

    // Google's OAuth 2.0 endpoint for requesting an access token
    var oauth2Endpoint = 'https://accounts.google.com/o/oauth2/v2/auth';

    // Create element to open OAuth 2.0 endpoint in new window.
    var form = document.createElement('form');
    form.setAttribute('method', 'GET'); // Send as a GET request.
    form.setAttribute('action', oauth2Endpoint);

    // Parameters to pass to OAuth 2.0 endpoint.
    var params = {'client_id': YOUR_CLIENT_ID,
                  'redirect_uri': YOUR_REDIRECT_URI,
                  'scope': 'https://www.googleapis.com/auth/drive.metadata.readonly https://www.googleapis.com/auth/calendar.readonly',
                  'state': state,
                  'include_granted_scopes': 'true',
                  'response_type': 'token'};

    // Add form parameters as hidden input values.
    for (var p in params) {
      var input = document.createElement('input');
      input.setAttribute('type', 'hidden');
      input.setAttribute('name', p);
      input.setAttribute('value', params[p]);
      form.appendChild(input);
    }

    // Add form to page and submit it to open the OAuth 2.0 endpoint.
    document.body.appendChild(form);
    form.submit();
  }
</script>

<button onclick="trySampleRequest();">Try sample request</button>
</body></html>

JavaScript 生成元の検証ルール

Google では、デベロッパーがアプリケーションのセキュリティを維持できるように、JavaScript の生成元に次の検証ルールを適用しています。JavaScript のオリジンは、次のルールに準拠している必要があります。 これらのルールで使用されるドメイン、ホスト、スキームの定義については、RFC 3986 セクション 3 をご覧ください。

検証規則
スキーム

JavaScript のオリジンでは、プレーン HTTP ではなく HTTPS スキームを使用する必要があります。localhost URI(localhost IP アドレス URI を含む)は、このルールの対象外です。

ホスト

ホストを未加工の IP アドレスにすることはできません。localhost の IP アドレスは、このルールの対象外です。

ドメイン
  • ホスト TLD(トップレベル ドメイン)は、Public Suffix List に含まれている必要があります。
  • ホストドメインを “googleusercontent.com” にすることはできません。
  • アプリがドメインを所有していない限り、JavaScript の生成元に URL 短縮サービス ドメイン(goo.gl など)を含めることはできません。
  • Userinfo

    JavaScript の生成元に userinfo サブコンポーネントを含めることはできません。

    [パス]

    JavaScript のオリジンにパス コンポーネントを含めることはできません。

    クエリ

    JavaScript のオリジンにクエリ コンポーネントを含めることはできません。

    Fragment

    JavaScript のオリジンにフラグメント コンポーネントを含めることはできません。

    文字数 JavaScript 生成元には、次の文字を含めることはできません。
    • ワイルドカード文字('*')
    • 印刷不可能な ASCII 文字
    • 無効なパーセント エンコード(パーセント記号の後に 2 桁の 16 進数が続く URL エンコード形式に準拠していないパーセント エンコード)
    • Null 文字(エンコードされた NULL 文字。例: %00、%C0%80)

    段階的な認可

    OAuth 2.0 プロトコルでは、アプリはスコープによって識別されるリソースへのアクセス認可をリクエストします。ユーザー エクスペリエンスのベスト プラクティスとして、リソースの承認は必要なときにリクエストすることをおすすめします。この方法を可能にするため、Google の認可サーバーは段階的な認可をサポートしています。この機能を使用すると、必要に応じてスコープをリクエストできます。ユーザーが新しいスコープの権限を付与すると、ユーザーがプロジェクトに付与したすべてのスコープを含むトークンと交換できる認証コードが返されます。

    たとえば、音楽トラックを試聴してミックスを作成できるアプリの場合、ログイン時に必要なリソースはごくわずかで、ログインするユーザーの名前だけで十分な場合があります。ただし、完成したミックスを保存するには、Google ドライブへのアクセスが必要になります。ほとんどのユーザーは、アプリが実際に必要としたときにのみ Google ドライブへのアクセスを求められるのが自然だと感じます。

    この場合、アプリはログイン時に openid スコープと profile スコープをリクエストして基本的なログインを実行し、その後、ミックスを保存する最初のリクエスト時に https://www.googleapis.com/auth/drive.file スコープをリクエストします。

    増分認可から取得したアクセス トークンには、次のルールが適用されます。

    • このトークンは、新しい統合された認可に組み込まれたスコープに対応するリソースにアクセスするために使用できます。
    • 結合された認可の更新トークンを使用してアクセス トークンを取得すると、アクセス トークンは結合された認可を表し、レスポンスに含まれる scope 値のいずれかに使用できます。
    • 結合された認可には、ユーザーが API プロジェクトに付与したすべてのスコープが含まれます。これは、付与が異なるクライアントからリクエストされた場合でも同様です。たとえば、ユーザーがアプリケーションのデスクトップ クライアントを使用して 1 つのスコープへのアクセスを許可し、その後、モバイル クライアントを介して同じアプリケーションに別のスコープを許可した場合、結合された認可には両方のスコープが含まれます。
    • 複合認証を表すトークンを取り消すと、関連付けられたユーザーに代わってその認証のすべてのスコープへのアクセス権が同時に取り消されます。

    以下のコードサンプルは、既存のアクセス トークンにスコープを追加する方法を示しています。この方法により、アプリで複数のアクセス トークンを管理する必要がなくなります。

    OAuth 2.0 エンドポイント

    既存のアクセス トークンにスコープを追加するには、Google の OAuth 2.0 サーバーへのリクエストに include_granted_scopes パラメータを含めます。

    次のコード スニペットは、その方法を示しています。このスニペットでは、アクセス トークンが有効なスコープがブラウザのローカル ストレージに保存されていることを前提としています。(実証済みの例 コードでは、ブラウザのローカル ストレージに oauth2-test-params.scope プロパティを設定することで、アクセス トークンが有効なスコープのリストを保存します)。

    このスニペットは、アクセス トークンが有効なスコープと、特定のクエリに使用するスコープを比較します。アクセス トークンがそのスコープをカバーしていない場合、OAuth 2.0 フローが開始されます。ここで、oauth2SignIn 関数は、ステップ 2 で提供されたもの(および後で完全な例で提供されるもの)と同じです。

    var SCOPE = 'https://www.googleapis.com/auth/drive.metadata.readonly';
    var params = JSON.parse(localStorage.getItem('oauth2-test-params'));
    
    var current_scope_granted = false;
    if (params.hasOwnProperty('scope')) {
      var scopes = params['scope'].split(' ');
      for (var s = 0; s < scopes.length; s++) {
        if (SCOPE == scopes[s]) {
          current_scope_granted = true;
        }
      }
    }
    
    if (!current_scope_granted) {
      oauth2SignIn(); // This function is defined elsewhere in this document.
    } else {
      // Since you already have access, you can proceed with the API request.
    }

    トークン失効

    ユーザーがアプリケーションに付与したアクセス権を取り消したい場合があります。ユーザーは、 アカウント設定にアクセスしてアクセス権を取り消すことができます。詳しくは、アカウントにアクセスできるサードパーティのアプリやサービスを管理するの「サイトやアプリのアクセス権を削除する」セクションをご覧ください。

    アプリケーションは、プログラムによって、付与されたアクセス権を取り消すこともできます。 プログラムによる取り消しは、ユーザーが登録を解除した場合、アプリケーションを削除した場合、アプリに必要な API リソースが大幅に変更された場合などに重要です。つまり、削除プロセスの一部として、アプリに以前付与された権限が削除されるように API リクエストを送信できます。

    OAuth 2.0 エンドポイント

    プログラムでトークンを取り消すには、アプリケーションから https://oauth2.googleapis.com/revoke にリクエストを送信し、パラメータとしてトークンを含めます。

    curl -d -X -POST --header "Content-type:application/x-www-form-urlencoded" \
            https://oauth2.googleapis.com/revoke?token={token}

    トークンは、アクセス トークンまたは更新トークンです。トークンがアクセス トークンであり、対応する更新トークンがある場合、更新トークンも取り消されます。

    取り消しが正常に処理されると、レスポンスの HTTP ステータス コードは 200 になります。エラー状態の場合、HTTP ステータス コード 400 がエラーコードとともに返されます。

    次の JavaScript スニペットは、Google API クライアント ライブラリ for JavaScript を使用せずに JavaScript でトークンを取り消す方法を示しています。トークンを取り消すための Google の OAuth 2.0 エンドポイントは、クロスオリジン リソース シェアリング(CORS)をサポートしていないため、コードはフォームを作成し、XMLHttpRequest() メソッドを使用してリクエストを送信するのではなく、フォームをエンドポイントに送信します。

    function revokeAccess(accessToken) {
      // Google's OAuth 2.0 endpoint for revoking access tokens.
      var revokeTokenEndpoint = 'https://oauth2.googleapis.com/revoke';
    
      // Create <form> element to use to POST data to the OAuth 2.0 endpoint.
      var form = document.createElement('form');
      form.setAttribute('method', 'post');
      form.setAttribute('action', revokeTokenEndpoint);
    
      // Add access token to the form so it is set as value of 'token' parameter.
      // This corresponds to the sample curl request, where the URL is:
      //      https://oauth2.googleapis.com/revoke?token={token}
      var tokenField = document.createElement('input');
      tokenField.setAttribute('type', 'hidden');
      tokenField.setAttribute('name', 'token');
      tokenField.setAttribute('value', accessToken);
      form.appendChild(tokenField);
    
      // Add form to page and submit it to actually revoke the token.
      document.body.appendChild(form);
      form.submit();
    }

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

    ユーザーのアカウントを保護するための追加の手順として、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

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