Google Identity Services に移行する

概要

Google API を呼び出すためのユーザーごとのアクセス トークンを取得するために、Google は複数の 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 に設定します。必要に応じて、コロンとキー値を追加して、セッション ストレージにもログを記録します。ログインして認証情報を受け取ったら、収集したログを確認するか、後で分析するためにバックエンドに送信します。たとえば、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 モジュールを読み込むインスタンスを削除しました。

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

クッキー

ユーザーの承認には、クッキーの使用は必要ありません。

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

認証情報

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

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

暗黙的フロー

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

次の Google ログイン JavaScript クライアントの参照削除します。

メソッド

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

認可コードフロー

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

セッション状態

これまで、Google ログインでは、次の方法でユーザーのログイン状態を管理していました。

ウェブアプリへのログイン状態とユーザー セッションの管理は、デベロッパーの責任となります。

次の 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置き換える:

オブジェクト:

  • TokenClientConfiggapi.auth2.AuthorizeConfig

メソッド:

  • google.accounts.oauth2.initTokenClient()gapi.auth2.init()

パラメータ:

  • gapi.auth2.AuthorizeConfig.login_hint: TokenClientConfig.login_hint
  • gapi.auth2.GoogleUser.getHostedDomain(): TokenClientConfig.hd

認可コードフロー

Code クライアントを初期化するの例に沿って、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 つのスコープ)を含める必要があります。増分認可のためにアプリを更新する方法について詳しくは、ユーザーの同意を処理する方法をご覧ください。

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

認証コードのフロー

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

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

トークンの処理

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

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

期限切れのトークン

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

トークン失効

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

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

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

きめ細かい権限により、ユーザーは個々のスコープを承認または拒否できます。複数のスコープにアクセスをリクエストする場合、各スコープは他のスコープとは独立して付与または拒否されます。ユーザーの選択に基づいて、アプリは個々のスコープに依存する機能を選択的に有効にします。

暗黙的フロー

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

オブジェクト:

  • TokenClient.TokenResponsegapi.auth2.AuthorizeResponse
  • TokenClient.TokenResponsegapi.auth2.AuthResponse

メソッド:

  • GoogleUser.hasGrantedScopes() は、google.accounts.oauth2.hasGrantedAllScopes() に置き換えます。
  • 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 エンドポイント

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

この例では、ユーザーのブラウザから 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() or requestAccessToken() 古いデータを新しいものに置換
GoogleUser.getBasicProfile() 削除代わりに ID トークンを使用してください。Google ログインからの移行をご覧ください。
GoogleUser.getGrantedScopes() hasGrantedAnyScope() 古いものを新しいものに置き換える
GoogleUser.getHostedDomain() 削除
GoogleUser.getId() 削除
GoogleUser.grantOfflineAccess() 削除し、認証コードフローに沿って操作します。
GoogleUser.grant() 削除
GoogleUser.hasGrantedScopes() hasGrantedAnyScope() 古いものを新しいものに置き換える
GoogleUser.isSignedIn() 削除
GoogleUser.reloadAuthResponse() requestAccessToken() 古いものを削除し、new を呼び出して期限切れまたは取り消し済みのアクセス トークンを置き換えます。
gapi.auth2 オブジェクトと関連するメソッド:
gapi.auth2.AuthorizeConfig オブジェクト TokenClientConfig または CodeClientConfig 古いものを新しいものに置き換える
gapi.auth2.AuthorizeResponse オブジェクト 削除
gapi.auth2.AuthResponse オブジェクト 削除
gapi.auth2.authorize() requestCode() or requestAccessToken() 古いものを新しいものに置き換える
gapi.auth2.ClientConfig() TokenClientConfig または CodeClientConfig 古いものを新しいものに置き換える
gapi.auth2.getAuthInstance() 削除
gapi.auth2.init() initTokenClient() or initCodeClient() 古いものを新しいものに置き換える
gapi.auth2.OfflineAccessOptions オブジェクト 削除
gapi.auth2.SignInOptions オブジェクト 削除
gapi.signin2 オブジェクトと関連するメソッド:
gapi.signin2.render() 削除g_id_signin 要素の HTML DOM の読み込み、または google.accounts.id.renderButton の JS 呼び出しによって、ユーザーの Google アカウントへのログインがトリガーされます。

認証情報の例

既存の認証情報

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

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"
    }
  }