Google Identity Services に移行する

概要

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

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

このガイドの内容:

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

Identity Services JavaScript ライブラリの変更点については、概要ユーザー認可の仕組みで主な用語とコンセプトを確認してください。

ユーザーの登録とログインの認証が必要な場合は、Google ログインからの移行をご覧ください。

認可フローを特定する

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

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

ウェブアプリが暗黙的フローを使用しているかどうか。

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

  • 実装は以下に基づいて行われます。

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

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

  • バックエンド プラットフォームが、ユーザーに代わって Google API を表示しなくても、それらの 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 ライブラリを追加して、ウェブアプリに 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 を使用する必要はありません。

ユーザー認証で Cookie を使用する方法について詳しくは、Google ログインからの移行をご覧ください。また、Google の他のプロダクトやサービスが Cookie を使用する方法については、Google が Cookie を使用する仕組みをご覧ください。

認証情報

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

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

暗黙的フロー

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

以下の Google ログイン JavaScript クライアントのリファレンス削除してください。

Methods

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

認可コードフロー

Identity Services は、ブラウザ内の認証情報を ID トークンとアクセス トークンに分離します。この変更は、バックエンド プラットフォームから Google OAuth 2.0 エンドポイントを直接呼び出したり、Google API 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_hintTokenClientConfig.login_hint に置き換えます。
  • gapi.auth2.GoogleUser.getHostedDomain()TokenClientConfig.hd を指定)

認可コードフロー

コード クライアントの初期化の例に沿って、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()
  • GoogleUser.reloadAuthResponse()TokenClient.requestAccessToken()

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 Unauthorized および invalid_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()
  • GoogleUser.getGrantedScopes()google.accounts.oauth2.hasGrantedAllScopes()

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 モードを以下に示します。

<!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() 古いアクセス トークンまたは新しいアクセス トークンを削除して、期限切れまたは取り消し済みのアクセス トークンを置き換えます。
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 エンドポイントの直接呼び出しでは、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"
    }
  }