Google の ID サービスへの移行

コレクションでコンテンツを整理 必要に応じて、コンテンツの保存と分類を行います。

概要

Google では、Google API を呼び出すユーザー アクセス トークンを取得するために、複数の JavaScript ライブラリを提供しています。

このガイドでは、これらのライブラリから Google Identity Services ライブラリに移行する手順について説明します。

このガイドに沿って以下を行います。

  • 非推奨のプラットフォーム ライブラリを Identity Services ライブラリに置き換えます。
  • API クライアント ライブラリを使用する場合は、非推奨の gapi.auth2 モジュール、そのメソッド、オブジェクトを削除し、同等の Identity Services に置き換えます。

Identity Services JavaScript ライブラリの変更点については、概要ユーザー認可の仕組みをご覧ください。

ユーザー登録とログインの認証については、Google ログインからの移行をご覧ください。

承認フローを特定する

考えられるユーザー認証フローには、暗黙的コードと認証コードの 2 つがあります。

ウェブアプリを確認し、現在使用されている承認フローの種類を特定します。

ウェブアプリが暗黙的フローを使用していることを示します。

ウェブアプリが認証コードフローを使用していることを示します。

  • 実装は次の条件に基づいて決まります。

  • アプリは、ユーザーのブラウザとバックエンド プラットフォームの両方で実行されます。

  • バックエンド プラットフォームは認証コード エンドポイントをホストします。

  • バックエンド プラットフォームは、存在することなしにユーザーに代わって Google API を呼び出しますが、オフライン モードとも呼ばれます。

  • 更新トークンは、バックエンド プラットフォームによって管理、保存されます。

場合によっては、コードベースが両方のフローをサポートすることがあります。

承認フローを選択する

移行を開始する前に、既存のフローを続行するか、別のフローを採用するかがニーズに合っているかどうかを判断する必要があります。

認可フローの選択で、2 つのフローの主な違いとトレードオフを理解してください。

ほとんどの場合、最高レベルのユーザー セキュリティを提供する認証コードフローをおすすめします。このフローを実装すると、プラットフォームが新しいオフライン機能を簡単に追加できるようになります。たとえば、アップデートをフェッチして、カレンダー、写真、定期購入などに関する重要な変更をユーザーに知らせることができます。

以下のセレクタを使用して承認フローを選択します。

暗黙的フロー

ユーザーがいるときにブラウザ内で使用するアクセス トークンを取得します。

暗黙的フローの例には、Identity Services への移行前と移行後にウェブアプリが表示されます。

認証コードフロー

Google が発行するユーザーごとの認証コードは、バックエンド プラットフォームに配信され、アクセス トークンと更新トークンと交換されます。

認可コードフローの例は、Identity Services への移行前と移行後のウェブアプリを示しています。

このガイド全体を通して、太字で示されている手順に沿って、既存の機能を追加削除更新置換します。

ブラウザ内ウェブアプリの変更

このセクションでは、Google Identity Services JavaScript ライブラリに移行する際に、ブラウザ ウェブアプリに行う変更を確認します。

影響を受けるコードとテストを特定する

デバッグ Cookie は、影響を受けるコードを見つけて、非推奨後の動作をテストできます。

大規模アプリや複雑なアプリでは、gapi.auth2 モジュールのサポート終了によって影響を受けるすべてのコードを見つけることが難しい場合があります。サポート終了が予定されている機能をコンソールに記録するには、G_AUTH2_MIGRATION Cookie の値を informational に設定します。必要に応じて、セッション ストレージにもロギングするためのコロンと Key-Value を追加します。ログインと認証情報の受信後、収集されたログをレビューして、後で分析するためにバックエンドに送信します。たとえば、informational:showauth2use は、オリジンと URL を showauth2use という名前のセッション ストレージ キーに保存します。

gapi.auth2 モジュールが読み込まれなくなったときにアプリの動作を確認するには、G_AUTH2_MIGRATION Cookie の値を enforced に設定します。これにより、適用日の前に非推奨後の動作をテストできます。

有効な G_AUTH2_MIGRATION Cookie 値:

  • enforced gapi.auth2 モジュールを読み込みません。
  • informational 非推奨の機能の使用を JS コンソールに記録します。オプションのキー名が設定されている場合は、セッション ストレージにログを記録します(informational:key-name)。

ユーザーへの影響を最小限に抑えるため、本番環境で使用する前に、開発とテストの際にこの Cookie をローカルで設定することをおすすめします。

ライブラリとモジュール

gapi.auth2 モジュールは、ログインに使用するユーザー認証と認可の暗黙的フローを管理します。この非推奨モジュールと、そのオブジェクトとメソッドを Google Identity Services ライブラリに置き換えます。

ドキュメントに Identity Services ライブラリを追加して、ウェブアプリに 追加します。

<script src="https://accounts.google.com/gsi/client" async defer></script>

gapi.load('auth2', function) を使用して auth2 モジュールを読み込むインスタンスを削除します。

gapi.auth2 モジュールの使用は、Google Identity Services ライブラリに置き換えられます。引き続き JavaScript 用 Google API クライアント ライブラリgapi.client モジュールを安全に使用し、検出ドキュメントからの呼び出し可能な JS メソッドの自動作成、複数の API 呼び出しのバッチ処理、CORS 管理機能を利用できます。

Cookie

ユーザー認証には Cookie を使用する必要はありません。

ユーザー認証で Cookie がどのように使用されるかについては、Google ログインからの移行をご覧ください。また、Google の他のプロダクトやサービスで使用される Cookie については、Google で Cookie がどのように使用されるかをご覧ください。

認証情報

Google Identity Services はユーザー認証と認可を 2 つの異なるオペレーションに分離します。ユーザー認証情報は、認証に使用されるアクセス トークンとは別に返されます。

この変更を確認するには、認証情報の例をご覧ください。

暗黙的フロー

ユーザー プロファイルの処理を認可フローから削除して、ユーザー認証と認可を分離します。

以下の Google ログイン JavaScript クライアント リファレンス削除します。

Methods

  • GoogleUser.getBasicProfile()
  • GoogleUser.getId()

認証コードフロー

Identity Services は、ブラウザ内の認証情報を ID トークンとアクセス トークンに分けます。この変更は、バックエンド プラットフォームから Google OAuth 2.0 エンドポイントへの直接呼び出しや、プラットフォーム上の安全なサーバー(Google API Node.js クライアントなど)で実行されているライブラリを通じて取得した認証情報には適用されません。

セッション状態

これまでは、以下を使用してユーザーのログイン ステータスを管理していました。

ウェブアプリのログイン ステータスとユーザー セッションの管理はお客様の責任となります。

以下の Google ログイン JavaScript クライアント リファレンス削除します。

オブジェクト:

  • gapi.auth2.SignInOptions

メソッド:

  • GoogleAuth.attachClickHandler()
  • GoogleAuth.isSignedIn()
  • GoogleAuth.isSignedIn.get()
  • GoogleAuth.isSignedIn.listen()
  • GoogleAuth.signIn()
  • GoogleAuth.signOut()
  • GoogleAuth.currentUser.get()
  • GoogleAuth.currentUser.listen()
  • GoogleUser.isSignedIn()

クライアントの構成

ウェブアプリを更新して、暗黙的または認証のコードフロー用にトークン クライアントを初期化します。

以下の Google ログイン JavaScript クライアント リファレンス削除します。

オブジェクト:

  • gapi.auth2.ClientConfig
  • gapi.auth2.OfflineAccessOptions

メソッド:

  • gapi.auth2.getAuthInstance()
  • GoogleUser.grant()

暗黙的フロー

トークン クライアントを初期化するの例に従って、TokenClientConfig オブジェクトと initTokenClient() 呼び出しを追加してウェブアプリを構成します。

Google ログインの JavaScript クライアント参照Google Identity Services置き換えます。

オブジェクト:

  • TokenClientConfig のある gapi.auth2.AuthorizeConfig

メソッド:

  • google.accounts.oauth2.initTokenClient() のある gapi.auth2.init()

パラメータ:

  • gapi.auth2.AuthorizeConfig.login_hintTokenClientConfig.hint を指定)
  • gapi.auth2.GoogleUser.getHostedDomain()TokenClientConfig.hosted_domain を指定)

認証コードフロー

コード クライアントを初期化するの例に従って、CodeClientConfig オブジェクトと initCodeClient() 呼び出しを追加してウェブアプリを構成します。

暗黙的コードから認証コードフローに切り替える場合:

削除 Google ログインの JavaScript クライアント リファレンス

オブジェクト:

  • gapi.auth2.AuthorizeConfig

メソッド:

  • gapi.auth2.init()

パラメータ:

  • gapi.auth2.AuthorizeConfig.login_hint
  • gapi.auth2.GoogleUser.getHostedDomain()

トークン リクエスト

ボタンのクリックなどのユーザー ジェスチャーにより、暗黙的トークンでブラウザに直接アクセス トークンが返されるようにリクエストや、ユーザーごとの認証コードをアクセス トークンおよび更新トークンと交換した後にバックエンド プラットフォームに返されるリクエストが生成されます。

暗黙的フロー

ユーザーがログインしていて Google とのアクティブなセッションがあるときは、ブラウザ内でアクセス トークンを取得して使用できます。暗黙的モードの場合、以前にリクエストがあった場合でも、ユーザー ジェスチャーでアクセス トークンをリクエストする必要があります。

Google ログインの JavaScript クライアント リファレンス Google Identity Services に置き換えます。

メソッド:

  • TokenClient.requestAccessToken() のある gapi.auth2.authorize()
  • TokenClient.requestAccessToken() のある GoogleUser.reloadAuthResponse()

リンクまたはボタンを追加して requestAccessToken() を呼び出し、アクセス トークンをリクエストするポップアップ UX フローを開始するか、既存のトークンの有効期限が切れたときに新しいトークンを取得します。

コードベースを更新します。

  • requestAccessToken() を使用して、OAuth 2.0 トークンフローをトリガーします。
  • requestAccessTokenOverridableTokenClientConfig を使用して、多くのスコープに対する 1 つのリクエストを複数の小さなリクエストに分割することで、段階的な認可をサポートします。
  • 既存のトークンが期限切れになるか取り消された場合に、新しいトークンをリクエストします。

複数のスコープを扱う際は、スコープへのアクセスを 1 回ではなく必要とするため、コードベースに構造上の変更を加える必要があります。これは段階的な承認と呼ばれます。各リクエストにはスコープをできるだけ少なく、理想的には 1 つに含める必要があります。アプリを段階的に更新して増分承認を取得する方法については、ユーザーの同意に対処する方法をご覧ください。

アクセス トークンが期限切れになると、gapi.auth2 モジュールはウェブアプリ用の新しい有効なアクセス トークンを自動的に取得します。ユーザーのセキュリティ強化のため、この自動トークン更新プロセスは Google Identity Services ライブラリでサポートされていません。有効期限が切れたアクセス トークンを検出して新しいトークンをリクエストするには、ウェブアプリを更新する必要があります。詳細については、以下のトークン処理のセクションをご覧ください。

認証コードフロー

requestCode() を呼び出して Google に認証コードをリクエストするためのリンクまたはボタンを追加します。例については、OAuth 2.0 コードフローのトリガーをご覧ください。

期限切れまたは取り消されたアクセス トークンに応答する方法について詳しくは、以下のトークン処理のセクションをご覧ください。

トークン処理

追加: 期限切れまたは取り消されたアクセス トークンが使用されたときに失敗した Google API 呼び出しを検出し、新しい有効なアクセス トークンをリクエストします。

期限切れまたは取り消されたアクセス トークンが使用されると、Google API から 401 Unauthorizedinvalid_token の HTTP ステータス コードが返されます。例については、無効なトークン レスポンスをご覧ください。

期限切れのトークン

アクセス トークンの有効期間は短く、多くの場合、有効期間は数分のみです。

トークンの取り消し

Google アカウント所有者はいつでも、以前付与された同意を取り消すことができます。この操作を行うと、既存のアクセス トークンと更新トークンが無効になります。取り消しは、revoke() を使用してプラットフォームから、または Google アカウントを介してトリガーできます。

Google ログインの JavaScript クライアント リファレンス Google Identity Services に置き換えます。

メソッド:

  • google.accounts.oauth2.revoke() のある getAuthInstance().disconnect()
  • google.accounts.oauth2.revoke() のある GoogleUser.disconnect()

ユーザーがプラットフォームでアカウントを削除した場合、またはアプリとのデータ共有への同意を削除するには、revoke を呼び出します。

ウェブアプリまたはバックエンド プラットフォームのいずれかがアクセス トークンをリクエストすると、同意ダイアログがユーザーに表示されます。Google がユーザーに表示する同意ダイアログの例をご覧ください。

アプリのアクセス トークンを発行する前に、既存のアクティブな Google セッションで、ユーザーの同意を求め、結果を記録する必要があります。既存のセッションがまだ確立されていない場合は、ユーザーが Google アカウントへのログインを求められることがあります。

ユーザーのログイン

ユーザーは、別のブラウザタブで Google アカウントにログインするか、ブラウザまたはオペレーティング システムからネイティブにログインできます。Google でログインをサイトに追加して、ユーザーが初めてアプリを開いたときに Google アカウントとブラウザ間のアクティブなセッションを確立することをおすすめします。これを行うと、次のようなメリットがあります。

  • ユーザーがログインする必要がある回数を最小限に抑える。アクセス トークンをリクエストすると、アクティブなセッションがまだ存在しない場合に Google アカウントのログイン プロセスが開始されます。
  • CodeClientConfig または TokenClientConfig オブジェクトの hint パラメータの値として、JWT ID トークンの認証情報 email フィールドを直接使用します。これは、プラットフォームでユーザー アカウント管理システムを維持しない場合に特に便利です。
  • プラットフォーム上の既存のローカル ユーザー アカウントを Google アカウントで検索して関連付けることで、プラットフォーム上の重複アカウントを最小限に抑えることができます。
  • 新しいローカル アカウントを作成すると、登録ダイアログと登録フローがユーザー認証ダイアログやユーザーフローから明確に分離されるため、必要な手順の数を減らし、離脱率を改善できます。

ログイン後、アクセス トークンを発行する前に、ユーザーはリクエストされたスコープについてアプリケーションに同意する必要があります。

同意されると、アクセス トークンはユーザーが承認または拒否したスコープのリストとともに返されます。

ユーザーは、きめ細かい権限を使用して、個々のスコープを承認または拒否できます。複数のスコープへのアクセスをリクエストする場合、各スコープは他のスコープとは無関係に許可または拒否されます。アプリは、ユーザーの選択に基づいて、個別のスコープに依存する機能を選択します。

暗黙的フロー

Google ログインの JavaScript クライアント参照Google Identity Services置き換えます。

オブジェクト:

  • TokenClient.TokenResponse のある gapi.auth2.AuthorizeResponse
  • TokenClient.TokenResponse のある gapi.auth2.AuthResponse

メソッド:

  • google.accounts.oauth2.hasGrantedAllScopes() のある GoogleUser.hasGrantedScopes()
  • google.accounts.oauth2.hasGrantedAllScopes() のある GoogleUser.getGrantedScopes()

Google ログインの JavaScript クライアント リファレンス削除します。

メソッド:

  • GoogleUser.getAuthResponse()

このきめ細かい権限の例に従って、hasGrantedAllScopes()hasGrantedAnyScope() でウェブアプリを更新します。

認証コードフロー

認証コードの処理の手順に沿って、バックエンド プラットフォームに認証エンドポイントを更新または追加します。

プラットフォームを更新して、コードモデルの使用ガイドの手順に沿って、リクエストを検証し、アクセス トークンと更新トークンを取得します。

プラットフォームを更新して、ユーザーが承認した個々のスコープに基づいて機能を個別に選択または有効または無効にします。手順については、増分承認ユーザーが付与したアクセス スコープを調べるをご覧ください。

暗黙的フローの例

従来の方法

GAPI クライアント ライブラリ

ユーザーの同意のためのポップアップ ダイアログを使用してブラウザで実行する JavaScript の Google API クライアント ライブラリの例。

gapi.auth2 モジュールは自動的に読み込まれ、gapi.client.init() によって使用されるため、非表示になります。

<!DOCTYPE html>
  <html>
    <head>
      <script src="https://apis.google.com/js/api.js"></script>
      <script>
        function start() {
          gapi.client.init({
            'apiKey': 'YOUR_API_KEY',
            'clientId': 'YOUR_CLIENT_ID',
            'scope': 'https://www.googleapis.com/auth/cloud-translation',
            'discoveryDocs': ['https://www.googleapis.com/discovery/v1/apis/translate/v2/rest'],
          }).then(function() {
            // Execute an API request which is returned as a Promise.
            // The method name language.translations.list comes from the API discovery.
            return gapi.client.language.translations.list({
              q: 'hello world',
              source: 'en',
              target: 'de',
            });
          }).then(function(response) {
            console.log(response.result.data.translations[0].translatedText);
          }, function(reason) {
            console.log('Error: ' + reason.result.error.message);
          });
        };

        // Load the JavaScript client library and invoke start afterwards.
        gapi.load('client', start);
      </script>
    </head>
    <body>
      <div id="results"></div>
    </body>
  </html>

JS クライアント ライブラリ

ユーザーの同意を確認するポップアップ ダイアログを使ってブラウザ内で実行するクライアント側ウェブ アプリケーション用の OAuth 2.0

gapi.auth2 モジュールは手動で読み込まれます。

<!DOCTYPE html>
<html><head></head><body>
<script>
  var GoogleAuth;
  var SCOPE = 'https://www.googleapis.com/auth/drive.metadata.readonly';
  function handleClientLoad() {
    // Load the API's client and auth2 modules.
    // Call the initClient function after the modules load.
    gapi.load('client:auth2', initClient);
  }

  function initClient() {
    // In practice, your app can retrieve one or more discovery documents.
    var discoveryUrl = 'https://www.googleapis.com/discovery/v1/apis/drive/v3/rest';

    // Initialize the gapi.client object, which app uses to make API requests.
    // Get API key and client ID from API Console.
    // 'scope' field specifies space-delimited list of access scopes.
    gapi.client.init({
        'apiKey': 'YOUR_API_KEY',
        'clientId': 'YOUR_CLIENT_ID',
        'discoveryDocs': [discoveryUrl],
        'scope': SCOPE
    }).then(function () {
      GoogleAuth = gapi.auth2.getAuthInstance();

      // Listen for sign-in state changes.
      GoogleAuth.isSignedIn.listen(updateSigninStatus);

      // Handle initial sign-in state. (Determine if user is already signed in.)
      var user = GoogleAuth.currentUser.get();
      setSigninStatus();

      // Call handleAuthClick function when user clicks on
      //      "Sign In/Authorize" button.
      $('#sign-in-or-out-button').click(function() {
        handleAuthClick();
      });
      $('#revoke-access-button').click(function() {
        revokeAccess();
      });
    });
  }

  function handleAuthClick() {
    if (GoogleAuth.isSignedIn.get()) {
      // User is authorized and has clicked "Sign out" button.
      GoogleAuth.signOut();
    } else {
      // User is not signed in. Start Google auth flow.
      GoogleAuth.signIn();
    }
  }

  function revokeAccess() {
    GoogleAuth.disconnect();
  }

  function setSigninStatus() {
    var user = GoogleAuth.currentUser.get();
    var isAuthorized = user.hasGrantedScopes(SCOPE);
    if (isAuthorized) {
      $('#sign-in-or-out-button').html('Sign out');
      $('#revoke-access-button').css('display', 'inline-block');
      $('#auth-status').html('You are currently signed in and have granted ' +
          'access to this app.');
    } else {
      $('#sign-in-or-out-button').html('Sign In/Authorize');
      $('#revoke-access-button').css('display', 'none');
      $('#auth-status').html('You have not authorized this app or you are ' +
          'signed out.');
    }
  }

  function updateSigninStatus() {
    setSigninStatus();
  }
</script>

<button id="sign-in-or-out-button"
        style="margin-left: 25px">Sign In/Authorize</button>
<button id="revoke-access-button"
        style="display: none; margin-left: 25px">Revoke access</button>

<div id="auth-status" style="display: inline; padding-left: 25px"></div><hr>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script async defer src="https://apis.google.com/js/api.js"
        onload="this.onload=function(){};handleClientLoad()"
        onreadystatechange="if (this.readyState === 'complete') this.onload()">
</script>
</body></html>

OAuth 2.0 エンドポイント

クライアントサイド ウェブ アプリケーション用の OAuth 2.0。ブラウザ内で実行され、ユーザーの同意を得るために Google へのリダイレクトが使用されます。

この例では、ブラウザから Google の OAuth 2.0 エンドポイントを直接呼び出します。gapi.auth2 モジュールと JavaScript ライブラリは使用しません。

<!DOCTYPE html>
<html><head></head><body>
<script>
  var YOUR_CLIENT_ID = 'REPLACE_THIS_VALUE';
  var YOUR_REDIRECT_URI = 'REPLACE_THIS_VALUE';
  var fragmentString = location.hash.substring(1);

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

  // 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']) {
      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 {
      oauth2SignIn();
    }
  }

  /*
   * Create form to request access token from Google's OAuth 2.0 server.
   */
  function oauth2SignIn() {
    // 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',
                  'state': 'try_sample_request',
                  '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>

新しい方法

GIS のみ

この例は、トークン モデルを使用する Google Identity Service JavaScript ライブラリと、ユーザーの同意のためのポップアップ ダイアログを示しています。ここでは、クライアントの構成、アクセス トークンのリクエストと取得、Google API の呼び出しを行うために必要な最小限のステップについて説明します。

<!DOCTYPE html>
<html>
  <head>
    <script src="https://accounts.google.com/gsi/client" onload="initClient()" async defer></script>
  </head>
  <body>
    <script>
      var client;
      var access_token;

      function initClient() {
        client = google.accounts.oauth2.initTokenClient({
          client_id: 'YOUR_CLIENT_ID',
          scope: 'https://www.googleapis.com/auth/calendar.readonly \
                  https://www.googleapis.com/auth/contacts.readonly',
          callback: (tokenResponse) => {
            access_token = tokenResponse.access_token;
          },
        });
      }
      function getToken() {
        client.requestAccessToken();
      }
      function revokeToken() {
        google.accounts.oauth2.revoke(access_token, () => {console.log('access token revoked')});
      }
      function loadCalendar() {
        var xhr = new XMLHttpRequest();
        xhr.open('GET', 'https://www.googleapis.com/calendar/v3/calendars/primary/events');
        xhr.setRequestHeader('Authorization', 'Bearer ' + access_token);
        xhr.send();
      }
    </script>
    <h1>Google Identity Services Authorization Token model</h1>
    <button onclick="getToken();">Get access token</button><br><br>
    <button onclick="loadCalendar();">Load Calendar</button><br><br>
    <button onclick="revokeToken();">Revoke token</button>
  </body>
</html>

GAPI async/await

この例では、トークン モデルを使用して Google Identity Service ライブラリを追加し、gapi.auth2 モジュールを削除し、JavaScript の Google API クライアント ライブラリを使用して API を呼び出す方法を示しています。

Promise の async と await は、ライブラリの読み込み順序の適用や、承認エラーのキャッチと再試行に使用されます。API 呼び出しは、有効なアクセス トークンが使用可能になった場合にのみ行われます。

ページが最初に読み込まれたときにアクセス トークンがない場合、またはアクセス トークンの有効期限が切れた後に、[カレンダーを表示] ボタンを押す必要があります。

<!DOCTYPE html>
<html>
<head></head>
<body>
  <h1>GAPI with GIS async/await</h1>
  <button id="showEventsBtn" onclick="showEvents();">Show Calendar</button><br><br>
  <button id="revokeBtn" onclick="revokeToken();">Revoke access token</button>

  <script>

    const gapiLoadPromise = new Promise((resolve, reject) => {
      gapiLoadOkay = resolve;
      gapiLoadFail = reject;
    });
    const gisLoadPromise = new Promise((resolve, reject) => {
      gisLoadOkay = resolve;
      gisLoadFail = reject;
    });

    var tokenClient;

    (async () => {
      document.getElementById("showEventsBtn").style.visibility="hidden";
      document.getElementById("revokeBtn").style.visibility="hidden";

      // First, load and initialize the gapi.client
      await gapiLoadPromise;
      await new Promise((resolve, reject) => {
        // NOTE: the 'auth2' module is no longer loaded.
        gapi.load('client', {callback: resolve, onerror: reject});
      });
      await gapi.client.init({
        // NOTE: OAuth2 'scope' and 'client_id' parameters have moved to initTokenClient().
      })
      .then(function() {  // Load the Calendar API discovery document.
        gapi.client.load('https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest');
      });

      // Now load the GIS client
      await gisLoadPromise;
      await new Promise((resolve, reject) => {
        try {
          tokenClient = google.accounts.oauth2.initTokenClient({
              client_id: 'YOUR_CLIENT_ID',
              scope: 'https://www.googleapis.com/auth/calendar.readonly',
              prompt: 'consent',
              callback: '',  // defined at request time in await/promise scope.
          });
          resolve();
        } catch (err) {
          reject(err);
        }
      });

      document.getElementById("showEventsBtn").style.visibility="visible";
      document.getElementById("revokeBtn").style.visibility="visible";
    })();

    async function getToken(err) {

      if (err.result.error.code == 401 || (err.result.error.code == 403) &&
          (err.result.error.status == "PERMISSION_DENIED")) {

        // The access token is missing, invalid, or expired, prompt for user consent to obtain one.
        await new Promise((resolve, reject) => {
          try {
            // Settle this promise in the response callback for requestAccessToken()
            tokenClient.callback = (resp) => {
              if (resp.error !== undefined) {
                reject(resp);
              }
              // GIS has automatically updated gapi.client with the newly issued access token.
              console.log('gapi.client access token: ' + JSON.stringify(gapi.client.getToken()));
              resolve(resp);
            };
            tokenClient.requestAccessToken();
          } catch (err) {
            console.log(err)
          }
        });
      } else {
        // Errors unrelated to authorization: server errors, exceeding quota, bad requests, and so on.
        throw new Error(err);
      }
    }

    function showEvents() {

      // Try to fetch a list of Calendar events. If a valid access token is needed,
      // prompt to obtain one and then retry the original request.
      gapi.client.calendar.events.list({ 'calendarId': 'primary' })
      .then(calendarAPIResponse => console.log(JSON.stringify(calendarAPIResponse)))
      .catch(err  => getToken(err))  // for authorization errors obtain an access token
      .then(retry => gapi.client.calendar.events.list({ 'calendarId': 'primary' }))
      .then(calendarAPIResponse => console.log(JSON.stringify(calendarAPIResponse)))
      .catch(err  => console.log(err));   // cancelled by user, timeout, etc.
    }

    function revokeToken() {
      let cred = gapi.client.getToken();
      if (cred !== null) {
        google.accounts.oauth2.revoke(cred.access_token, () => {console.log('Revoked: ' + cred.access_token)});
        gapi.client.setToken('');
      }
    }

  </script>

  <script async defer src="https://apis.google.com/js/api.js" onload="gapiLoadOkay()" onerror="gapiLoadFail(event)"></script>
  <script async defer src="https://accounts.google.com/gsi/client" onload="gisLoadOkay()" onerror="gisLoadFail(event)"></script>

</body>
</html>

GAPI コールバック

この例では、トークン モデルを使用して Google Identity Service ライブラリを追加し、gapi.auth2 モジュールを削除し、JavaScript の Google API クライアント ライブラリを使用して API を呼び出す方法を示しています。

ライブラリの読み込み順序を適用するために変数を使用します。有効なアクセス トークンが返された後、コールバック内から GAPI 呼び出しが行われます。

ユーザーが最初にページを読み込んだときに [カレンダーを表示] ボタンを押した後、再度カレンダー情報を更新するときに、このボタンを押す必要があります。

<!DOCTYPE html>
<html>
<head>
  <script async defer src="https://apis.google.com/js/api.js" onload="gapiLoad()"></script>
  <script async defer src="https://accounts.google.com/gsi/client" onload="gisInit()"></script>
</head>
<body>
  <h1>GAPI with GIS callbacks</h1>
  <button id="showEventsBtn" onclick="showEvents();">Show Calendar</button><br><br>
  <button id="revokeBtn" onclick="revokeToken();">Revoke access token</button>
  <script>
    let tokenClient;
    let gapiInited;
    let gisInited;

    document.getElementById("showEventsBtn").style.visibility="hidden";
    document.getElementById("revokeBtn").style.visibility="hidden";

    function checkBeforeStart() {
       if (gapiInited && gisInited){
          // Start only when both gapi and gis are initialized.
          document.getElementById("showEventsBtn").style.visibility="visible";
          document.getElementById("revokeBtn").style.visibility="visible";
       }
    }

    function gapiInit() {
      gapi.client.init({
        // NOTE: OAuth2 'scope' and 'client_id' parameters have moved to initTokenClient().
      })
      .then(function() {  // Load the Calendar API discovery document.
        gapi.client.load('https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest');
        gapiInited = true;
        checkBeforeStart();
      });
    }

    function gapiLoad() {
        gapi.load('client', gapiInit)
    }

    function gisInit() {
     tokenClient = google.accounts.oauth2.initTokenClient({
                client_id: 'YOUR_CLIENT_ID',
                scope: 'https://www.googleapis.com/auth/calendar.readonly',
                callback: '',  // defined at request time
            });
      gisInited = true;
      checkBeforeStart();
    }

    function showEvents() {

      tokenClient.callback = (resp) => {
        if (resp.error !== undefined) {
          throw(resp);
        }
        // GIS has automatically updated gapi.client with the newly issued access token.
        console.log('gapi.client access token: ' + JSON.stringify(gapi.client.getToken()));

        gapi.client.calendar.events.list({ 'calendarId': 'primary' })
        .then(calendarAPIResponse => console.log(JSON.stringify(calendarAPIResponse)))
        .catch(err => console.log(err));

        document.getElementById("showEventsBtn").innerText = "Refresh Calendar";
      }

      // Conditionally ask users to select the Google Account they'd like to use,
      // and explicitly obtain their consent to fetch their Calendar.
      // NOTE: To request an access token a user gesture is necessary.
      if (gapi.client.getToken() === null) {
        // Prompt the user to select a Google Account and asked for consent to share their data
        // when establishing a new session.
        tokenClient.requestAccessToken({prompt: 'consent'});
      } else {
        // Skip display of account chooser and consent dialog for an existing session.
        tokenClient.requestAccessToken({prompt: ''});
      }
    }

    function revokeToken() {
      let cred = gapi.client.getToken();
      if (cred !== null) {
        google.accounts.oauth2.revoke(cred.access_token, () => {console.log('Revoked: ' + cred.access_token)});
        gapi.client.setToken('');
        document.getElementById("showEventsBtn").innerText = "Show Calendar";
      }
    }
  </script>
</body>
</html>

認証コードフローの例

Google Identity Service ライブラリのポップアップ UX では、URL リダイレクトを使用して認証トークンをバックエンド トークン エンドポイントに直接返すか、ユーザーのブラウザで実行されている JavaScript コールバック ハンドラを使用して、レスポンスをプラットフォームにプロキシします。いずれの場合も、バックエンド プラットフォームは OAuth 2.0 フローを完了して、有効な更新トークンとアクセス トークンを取得します。

従来の方法

サーバーサイド ウェブアプリ

ユーザーの同意のために Google へのリダイレクトを使用するバックエンド プラットフォームで実行されているサーバー側アプリ向け Google ログイン

<!DOCTYPE html>
<html>
  <head>
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
    <script src="https://apis.google.com/js/client:platform.js?onload=start" async defer></script>
    <script>
      function start() {
        gapi.load('auth2', function() {
          auth2 = gapi.auth2.init({
            client_id: 'YOUR_CLIENT_ID',
            api_key: 'YOUR_API_KEY',
            discovery_docs: ['https://www.googleapis.com/discovery/v1/apis/translate/v2/rest'],
            // Scopes to request in addition to 'profile' and 'email'
            scope: 'https://www.googleapis.com/auth/cloud-translation',
          });
        });
      }
      function signInCallback(authResult) {
        if (authResult['code']) {
          console.log("sending AJAX request");
          // Send authorization code obtained from Google to backend platform
          $.ajax({
            type: 'POST',
            url: 'YOUR_AUTHORIZATION_CODE_ENDPOINT_URL',
            // Always include an X-Requested-With header to protect against CSRF attacks.
            headers: {
              'X-Requested-With': 'XMLHttpRequest'
            },
            contentType: 'application/octet-stream; charset=utf-8',
            success: function(result) {
              console.log(result);
            },
            processData: false,
            data: authResult['code']
          });
        } else {
          console.log('error: failed to obtain authorization code')
        }
      }
    </script>
  </head>
  <body>
    <button id="signinButton">Sign In With Google</button>
    <script>
      $('#signinButton').click(function() {
        // Obtain an authorization code from Google
        auth2.grantOfflineAccess().then(signInCallback);
      });
    </script>
  </body>
</html>

リダイレクトを使用する HTTP/REST

ウェブサーバー アプリケーションに OAuth 2.0 を使用して、ユーザーのブラウザからバックエンド プラットフォームに認証コードを送信します。ユーザーの同意は、ユーザーのブラウザを Google にリダイレクトすることで処理されます。

/\*
 \* 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 &lt;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_AUTHORIZATION_CODE_ENDPOINT_URL',
                'response\_type': 'token',
                'scope': 'https://www.googleapis.com/auth/drive.metadata.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();
}

新しい方法

GIS ポップアップ UX

この例では、Google Identity Service JavaScript ライブラリのみが、認証コードモデルを使用して、Google から認証コードを受け取るためのユーザーの同意ダイアログとコールバック ハンドラのポップアップ ダイアログを示しています。このドキュメントは、クライアントを構成して同意を取得し、認証コードをバックエンド プラットフォームに送信するために必要な最小限のステップを示しています。

<!DOCTYPE html>
<html>
  <head>
    <script src="https://accounts.google.com/gsi/client" onload="initClient()" async defer></script>
  </head>
  <body>
    <script>
      var client;
      function initClient() {
        client = google.accounts.oauth2.initCodeClient({
          client_id: 'YOUR_CLIENT_ID',
          scope: 'https://www.googleapis.com/auth/calendar.readonly',
          ux_mode: 'popup',
          callback: (response) => {
            var code_receiver_uri = 'YOUR_AUTHORIZATION_CODE_ENDPOINT_URI',
            // Send auth code to your backend platform
            const xhr = new XMLHttpRequest();
            xhr.open('POST', code_receiver_uri, true);
            xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
            xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
            xhr.onload = function() {
              console.log('Signed in as: ' + xhr.responseText);
            };
            xhr.send('code=' + response.code);
            // After receipt, the code is exchanged for an access token and
            // refresh token, and the platform then updates this web app
            // running in user's browser with the requested calendar info.
          },
        });
      }
      function getAuthCode() {
        // Request authorization code and obtain user consent
          client.requestCode();
      }
    </script>
    <button onclick="getAuthCode();">Load Your Calendar</button>
  </body>
</html>

GIS リダイレクト UX

認証コードモデルは、ポップアップとリダイレクトの UX モードをサポートしており、プラットフォームごとにホストされているエンドポイントにユーザーごとの認証コードを送信できます。リダイレクト UX モードは次のとおりです。

<!DOCTYPE html>
<html>
  <head>
    <script src="https://accounts.google.com/gsi/client" onload="initClient()" async defer></script>
  </head>
  <body>
    <script>
      var client;
      function initClient() {
        client = google.accounts.oauth2.initCodeClient({
          client_id: 'YOUR_CLIENT_ID',
          scope: 'https://www.googleapis.com/auth/calendar.readonly \
                  https://www.googleapis.com/auth/photoslibrary.readonly',
          ux_mode: 'redirect',
          redirect_uri: 'YOUR_AUTHORIZATION_CODE_ENDPOINT_URI'
        });
      }
      // Request an access token
      function getAuthCode() {
        // Request authorization code and obtain user consent
          client.requestCode();
      }
    </script>
    <button onclick="getAuthCode();">Load Your Calendar</button>
  </body>
</html>

JavaScript ライブラリ

Google Identity Services は、ユーザーの認証と認可に使用される単一の JavaScript ライブラリであり、複数の異なるライブラリやモジュールに備わっている機能を統合して置き換えます。

Identity Services への移行時に取るべきアクション:

既存の JS ライブラリ 新しい JS ライブラリ メモ
apis.google.com/js/api.js accounts.google.com/gsi/client 新しいライブラリを追加し、暗黙的なフローに従います。
apis.google.com/js/client.js accounts.google.com/gsi/client 新しいライブラリと認証コードフローを追加する。

ライブラリ クイック リファレンス

古い Google ログインの JavaScript クライアント ライブラリと、新しい Google Identity Services ライブラリおよびメモの間のオブジェクトとメソッドの比較。移行時に使用する情報と詳細情報が記載されています。

新規 メモ
GoogleAuth オブジェクトと関連メソッド:
GoogleAuth.attachClickHandler() 削除
GoogleAuth.currentUser.get() 削除
GoogleAuth.currentUser.listen() 削除
GoogleAuth.disconnect() google.accounts.oauth2.revoke 古いデバイスを新しいものと交換します。https://myaccount.google.com/permissions から取り消すこともできます。
GoogleAuth.grantOfflineAccess() 認証コードフローを削除し、それに従います。
GoogleAuth.isSignedIn.get() 削除
GoogleAuth.isSignedIn.listen() 削除
GoogleAuth.signIn() 削除
GoogleAuth.signOut() 削除
GoogleAuth.then() 削除
GoogleUser オブジェクトと関連メソッド:
GoogleUser.disconnect() google.accounts.id.revoke 古いデバイスを新しいものと交換します。https://myaccount.google.com/permissions から取り消すこともできます。
GoogleUser.getAuthResponse() requestCode() または requestAccessToken() 古いものと置き換える
GoogleUser.getBasicProfile() 削除ID トークンを使用する場合は、Google ログインからの移行をご覧ください。
GoogleUser.getGrantedScopes() hasGrantedAnyScope() 古いものと置き換える
GoogleUser.getHostedDomain() 削除
GoogleUser.getId() 削除
GoogleUser.grantOfflineAccess() 認証コードフローを削除し、それに従います。
GoogleUser.grant() 削除
GoogleUser.hasGrantedScopes() hasGrantedAnyScope() 古いものと置き換える
GoogleUser.isSignedIn() 削除
GoogleUser.reloadAuthResponse() requestAccessToken() 古い、または新しく取り消されたアクセス トークンを削除します。
gapi.auth2 オブジェクトと関連メソッド:
gapi.auth2.AuthorizeConfig オブジェクト TokenClientConfig または CodeClientConfig 古いものと置き換える
gapi.auth2.AuthorizeResponse オブジェクト 削除
gapi.auth2.AuthResponse オブジェクト 削除
gapi.auth2.authorization() requestCode() または requestAccessToken() 古いものと置き換える
gapi.auth2.ClientConfig() TokenClientConfig または CodeClientConfig 古いものと置き換える
gapi.auth2.getAuthInstance() 削除
gapi.auth2.init() initTokenClient() または initCodeClient() 古いものと置き換える
gapi.auth2.OfflineAccessOptions オブジェクト 削除
gapi.auth2.SignInOptions オブジェクト 削除
gapi.signin2 オブジェクトと関連メソッド:
gapi.signin2.render() 削除g_id_signin 要素または google.accounts.id.renderButton に対する JS 呼び出しを HTML DOM で読み込むと、ユーザーの Google アカウントへのログインがトリガーされます。

認証情報の例

既存の認証情報

Google ログイン プラットフォーム ライブラリJavaScript の Google API クライアント ライブラリ、または Google Auth 2.0 エンドポイントへの直接呼び出しは、1 つのレスポンスで OAuth 2.0 アクセス トークンと OpenID Connect ID トークンの両方を返します。

access_tokenid_token の両方を含むレスポンスの例:

  {
    "token_type": "Bearer",
    "access_token": "ya29.A0ARrdaM-SmArZaCIh68qXsZSzyeU-8mxhQERHrP2EXtxpUuZ-3oW8IW7a6D2J6lRnZrRj8S6-ZcIl5XVEqnqxq5fuMeDDH_6MZgQ5dgP7moY-yTiKR5kdPm-LkuPM-mOtUsylWPd1wpRmvw_AGOZ1UUCa6UD5Hg",
    "scope": "https://www.googleapis.com/auth/calendar.readonly",
    "login_hint": "AJDLj6I2d1RH77cgpe__DdEree1zxHjZJr4Q7yOisoumTZUmo5W2ZmVFHyAomUYzLkrluG-hqt4RnNxrPhArd5y6p8kzO0t8xIfMAe6yhztt6v2E-_Bb4Ec3GLFKikHSXNh5bI-gPrsI",
    "expires_in": 3599,
    "id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjkzNDFhYmM0MDkyYjZmYzAzOGU0MDNjOTEwMjJkZDNlNDQ1MzliNTYiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJhY2NvdW50cy5nb29nbGUuY29tIiwiYXpwIjoiNTM4MzQ0NjUzMjU1LTc1OGM1aDVpc2M0NXZnazI3ZDhoOGRlYWJvdnBnNnRvLmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tIiwiYXVkIjoiNTM4MzQ0NjUzMjU1LTc1OGM1aDVpc2M0NXZnazI3ZDhoOGRlYWJvdnBnNnRvLmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tIiwic3ViIjoiMTE3NzI2NDMxNjUxOTQzNjk4NjAwIiwiaGQiOiJnb29nbGUuY29tIiwiZW1haWwiOiJkYWJyaWFuQGdvb2dsZS5jb20iLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiYXRfaGFzaCI6IkJBSW55TjN2MS1ZejNLQnJUMVo0ckEiLCJuYW1lIjoiQnJpYW4gRGF1Z2hlcnR5IiwicGljdHVyZSI6Imh0dHBzOi8vbGgzLmdvb2dsZXVzZXJjb250ZW50LmNvbS9hLS9BT2gxNEdnenAyTXNGRGZvbVdMX3VDemRYUWNzeVM3ZGtxTE5ybk90S0QzVXNRPXM5Ni1jIiwiZ2l2ZW5fbmFtZSI6IkJyaWFuIiwiZmFtaWx5X25hbWUiOiJEYXVnaGVydHkiLCJsb2NhbGUiOiJlbiIsImlhdCI6MTYzODk5MTYzOCwiZXhwIjoxNjM4OTk1MjM4LCJqdGkiOiI5YmRkZjE1YWFiNzE2ZDhjYmJmNDYwMmM1YWM3YzViN2VhMDQ5OTA5In0.K3EA-3Adw5HA7O8nJVCsX1HmGWxWzYk3P7ViVBb4H4BoT2-HIgxKlx1mi6jSxIUJGEekjw9MC-nL1B9Asgv1vXTMgoGaNna0UoEHYitySI23E5jaMkExkTSLtxI-ih2tJrA2ggfA9Ekj-JFiMc6MuJnwcfBTlsYWRcZOYVw3QpdTZ_VYfhUu-yERAElZCjaAyEXLtVQegRe-ymScra3r9S92TA33ylMb3WDTlfmDpWL0CDdDzby2asXYpl6GQ7SdSj64s49Yw6mdGELZn5WoJqG7Zr2KwIGXJuSxEo-wGbzxNK-mKAiABcFpYP4KHPEUgYyz3n9Vqn2Tfrgp-g65BQ",
    "session_state": {
      "extraQueryParams": {
        "authuser": "0"
      }
    },
    "first_issued_at": 1638991637982,
    "expires_at": 1638995236982,
    "idpId": "google"
  }

Google Identity Services 認証情報

Google Identity Services ライブラリから次の結果が返されます。

  • 承認に使用される場合はアクセス トークンのいずれかになります。
  {
    "access_token": "ya29.A0ARrdaM_LWSO-uckLj7IJVNSfnUityT0Xj-UCCrGxFQdxmLiWuAosnAKMVQ2Z0LLqeZdeJii3TgULp6hR_PJxnInBOl8UoUwWoqsrGQ7-swxgy97E8_hnzfhrOWyQBmH6zs0_sUCzwzhEr_FAVqf92sZZHphr0g",
    "token_type": "Bearer",
    "expires_in": 3599,
    "scope": "https://www.googleapis.com/auth/calendar.readonly"
  }
  • または、認証に使用される場合の ID トークン:
  {
  "clientId": "538344653255-758c5h5isc45vgk27d8h8deabovpg6to.apps.googleusercontent.com",
  "credential": "eyJhbGciOiJSUzI1NiIsImtpZCI6ImMxODkyZWI0OWQ3ZWY5YWRmOGIyZTE0YzA1Y2EwZDAzMjcxNGEyMzciLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJuYmYiOjE2MzkxNTcyNjQsImF1ZCI6IjUzODM0NDY1MzI1NS03NThjNWg1aXNjNDV2Z2syN2Q4aDhkZWFib3ZwZzZ0by5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsInN1YiI6IjExNzcyNjQzMTY1MTk0MzY5ODYwMCIsIm5vbmNlIjoiZm9vYmFyIiwiaGQiOiJnb29nbGUuY29tIiwiZW1haWwiOiJkYWJyaWFuQGdvb2dsZS5jb20iLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiYXpwIjoiNTM4MzQ0NjUzMjU1LTc1OGM1aDVpc2M0NXZnazI3ZDhoOGRlYWJvdnBnNnRvLmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tIiwibmFtZSI6IkJyaWFuIERhdWdoZXJ0eSIsInBpY3R1cmUiOiJodHRwczovL2xoMy5nb29nbGV1c2VyY29udGVudC5jb20vYS0vQU9oMTRHZ3pwMk1zRkRmb21XTF91Q3pkWFFjc3lTN2RrcUxOcm5PdEtEM1VzUT1zOTYtYyIsImdpdmVuX25hbWUiOiJCcmlhbiIsImZhbWlseV9uYW1lIjoiRGF1Z2hlcnR5IiwiaWF0IjoxNjM5MTU3NTY0LCJleHAiOjE2MzkxNjExNjQsImp0aSI6IjRiOTVkYjAyZjU4NDczMmUxZGJkOTY2NWJiMWYzY2VhYzgyMmI0NjUifQ.Cr-AgMsLFeLurnqyGpw0hSomjOCU4S3cU669Hyi4VsbqnAV11zc_z73o6ahe9Nqc26kPVCNRGSqYrDZPfRyTnV6g1PIgc4Zvl-JBuy6O9HhClAK1HhMwh1FpgeYwXqrng1tifmuotuLQnZAiQJM73Gl-J_6s86Buo_1AIx5YAKCucYDUYYdXBIHLxrbALsA5W6pZCqqkMbqpTWteix-G5Q5T8LNsfqIu_uMBUGceqZWFJALhS9ieaDqoxhIqpx_89QAr1YlGu_UO6R6FYl0wDT-nzjyeF5tonSs3FHN0iNIiR3AMOHZu7KUwZaUdHg4eYkU-sQ01QNY_11keHROCRQ",
  "select_by": "user"
  }

無効なトークン レスポンス

期限切れ、取り消し済み、または無効なアクセス トークンを使用して API リクエストを実行しようとしたときの Google からのレスポンスの例:

HTTP レスポンス ヘッダー

  www-authenticate: Bearer realm="https://accounts.google.com/", error="invalid_token"

レスポンスの本文

  {
    "error": {
      "code": 401,
      "message": "Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.",
      "errors": [
        {
          "message": "Invalid Credentials",
          "domain": "global",
          "reason": "authError",
          "location": "Authorization",
          "locationType": "header"
        }
      ],
      "status": "UNAUTHENTICATED"
    }
  }