Google Identity Services に移行する

概要

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

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

このガイドでは、次のことを行います。

  • 非推奨の Platform Library をアカウント管理サービス ライブラリに置き換える
  • API クライアント ライブラリを使用している場合は、非推奨の gapi.auth2 モジュール、そのメソッド、オブジェクトを削除し、アカウント管理サービスの同等のものに置き換えます。

アカウント管理サービス JavaScript ライブラリの変更内容については、概要とユーザー認証の仕組みで、重要な用語とコンセプトを確認してください。

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

認可フローを特定する

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

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

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

ウェブアプリが認可コードフローを使用していることを示す兆候:

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

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

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

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

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

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

認可フローを選択する

移行を開始する前に、既存のフローを継続するか、別のフローを採用するかを判断する必要があります。

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

ほとんどの場合、ユーザー セキュリティのレベルが最も高い認可コードフローをおすすめします。このフローを実装すると、プラットフォームに新しいオフライン機能を追加することもできます。たとえば、更新を取得して、カレンダー、写真、定期購入の重要な変更をユーザーに通知できます。

セレクタを使用して認可フローを選択します。

暗黙的フロー

ユーザーがログインしている間に、ブラウザ内で使用するアクセス トークンを取得します。

暗黙的フローの例では、アカウント管理サービス への移行前と移行後のウェブアプリを示しています。

認可コードフロー

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

認可コードフローの例では、アカウント管理サービス への移行前後のウェブアプリを示しています。

このガイドでは、既存の機能の追加、削除、更新、置換を行う手順を太字で示しています。

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

このセクションでは、Google Identity Services JavaScript ライブラリに移行する際に、ブラウザ内ウェブアプリに加える変更について説明します。

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

デバッグ Cookie は、影響を受けるコードの特定と、サポート終了後の動作のテストに役立ちます。

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

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 を使用する必要はありません。

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

認証情報

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

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

暗黙的フロー

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

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

メソッド

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

認可コードフロー

アカウント管理サービスは、ブラウザ内の認証情報を 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 に置き換えます。

オブジェクト:

  • TokenClientConfig で gapi.auth2.AuthorizeConfig

メソッド:

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

パラメータ:

  • gapi.auth2.AuthorizeConfig.login_hint は TokenClientConfig.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 トークン フローをトリガーします。
  • requestAccessToken と OverridableTokenClientConfig を使用して、多くのスコープに対する 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 でログインを追加することをおすすめします。これにより、次のようなメリットがあります。

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

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

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

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

暗黙的フロー

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

オブジェクト:

  • TokenClient.TokenResponse で gapi.auth2.AuthorizeResponse
  • TokenClient.TokenResponse で gapi.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 Google Cloud 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 the Google authorization 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 the Google authorization 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 呼び出しは、有効なアクセス トークンが利用可能になった後にのみ行われます。

ユーザーは、ページの初回読み込み時にアクセス トークンがない場合、またはアクセス トークンの有効期限が切れた後に、[Show Calendar] ボタンを押す必要があります。

<!DOCTYPE html>
<html>
<head>
    <title>GAPI and GIS Example</title>
    <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="gisLoad()"></script>
</head>
<body>
    <h1>GAPI Client with GIS Authorization</h1>
    <button id="authorizeBtn" style="visibility:hidden;">Authorize and Load Events</button>
    <button id="revokeBtn" style="visibility:hidden;">Revoke Access</button>
    <div id="content"></div>

    <script>
        const YOUR_CLIENT_ID = "YOUR_CLIENT_ID";
        const YOUR_API_KEY = 'YOUR_API_KEY';
        const CALENDAR_SCOPE = 'https://www.googleapis.com/auth/calendar.readonly';

        let tokenClient;
        let libsLoaded = 0;

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

        async function initGapiClient() {
            try {
                await gapi.client.init({ apiKey: YOUR_API_KEY });
                await gapi.client.load('https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest');
                console.log('GAPI client initialized.');
                checkAllLoaded();
            } catch (err) {
                handleError('GAPI initialization failed:', err);
            }
        }

        function gisLoad() {
            try {
                tokenClient = google.accounts.oauth2.initTokenClient({
                    client_id: YOUR_CLIENT_ID,
                    scope: CALENDAR_SCOPE,
                    callback: '', // Will be set dynamically
                    error_callback: handleGisError,
                });
                console.log('GIS TokenClient initialized.');
                checkAllLoaded();
            } catch (err) {
                handleError('GIS initialization failed:', err);
            }
        }

        function checkAllLoaded() {
            libsLoaded++;
            if (libsLoaded === 2) {
                document.getElementById('authorizeBtn').style.visibility = 'visible';
                document.getElementById('revokeBtn').style.visibility = 'visible';
                document.getElementById('authorizeBtn').onclick = makeApiCall;
                document.getElementById('revokeBtn').onclick = revokeAccess;
                console.log('Ready to authorize.');
            }
        }

        function handleGisError(err) {
            console.error('GIS Error:', err);
            let message = 'An error occurred during authorization.';
            if (err && err.type === 'popup_failed_to_open') {
                message = 'Failed to open popup. Please disable popup blockers.';
            } else if (err && err.type === 'popup_closed') {
                message = 'Authorization popup was closed.';
            }
            document.getElementById('content').textContent = message;
        }

        function handleError(message, error) {
            console.error(message, error);
            document.getElementById('content').textContent = `${message} ${error.message || JSON.stringify(error)}`;
        }

        async function makeApiCall() {
            document.getElementById('content').textContent = 'Processing...';
            try {
                let token = gapi.client.getToken();
                if (!token || !token.access_token) {
                    console.log('No token, fetching one...');
                    await getToken();
                }

                console.log('Calling Calendar API...');
                const response = await gapi.client.calendar.events.list({ 'calendarId': 'primary' });
                displayEvents(response.result);
            } catch (err) {
                console.error('API call failed:', err);
                const errorInfo = err.result && err.result.error;
                if (errorInfo && (errorInfo.code === 401 || (errorInfo.code === 403 && errorInfo.status === "PERMISSION_DENIED"))) {
                    console.log('Auth error on API call, refreshing token...');
                    try {
                        await getToken({ prompt: 'consent' }); // Force refresh
                        const retryResponse = await gapi.client.calendar.events.list({ 'calendarId': 'primary' });
                        displayEvents(retryResponse.result);
                    } catch (refreshErr) {
                        handleError('Failed to refresh token or retry API call:', refreshErr);
                    }
                } else {
                    handleError('Error loading events:', err.result ? err.result.error : err);
                }
            }
        }

        async function getToken(options = { prompt: '' }) {
            return new Promise((resolve, reject) => {
                if (!tokenClient) return reject(new Error("GIS TokenClient not initialized."));
                tokenClient.callback = (tokenResponse) => {
                    if (tokenResponse.error) {
                        reject(new Error(`Token Error: ${tokenResponse.error} - ${tokenResponse.error_description}`));
                    } else {
                        console.log('Token acquired.');
                        resolve(tokenResponse);
                    }
                };
                tokenClient.requestAccessToken(options);
            });
        }

        function displayEvents(result) {
            const events = result.items;
            if (events && events.length > 0) {
                let eventList = '<h3>Upcoming Events:</h3><ul>' + events.map(event =>
                    `<li>${event.summary} (${event.start.dateTime || event.start.date})</li>`
                ).join('') + '</ul>';
                document.getElementById('content').innerHTML = eventList;
            } else {
                document.getElementById('content').textContent = 'No upcoming events found.';
            }
        }

        function revokeAccess() {
            const token = gapi.client.getToken();
            if (token && token.access_token) {
                google.accounts.oauth2.revoke(token.access_token, () => {
                    console.log('Access revoked.');
                    document.getElementById('content').textContent = 'Access has been revoked.';
                    gapi.client.setToken(null);
                });
            } else {
                document.getElementById('content').textContent = 'No token to revoke.';
            }
        }
    </script>
</body>
</html>

GAPI コールバック

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

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

ユーザーは、ページが最初に読み込まれたときと、カレンダー情報を更新したいときに、[Show Calendar] ボタンを押す必要があります。

<!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 the Google authorization 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 から認可コードを受け取るためのコールバック ハンドラを表示する Google Identity Service JavaScript ライブラリのみを示しています。これは、クライアントの構成、同意の取得、バックエンド プラットフォームへの認証コードの送信に必要な最小限の手順を示すために提供されています。

<!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 ライブラリです。複数の異なるライブラリやモジュールに含まれる機能が統合され、置き換えられています。

アカウント管理サービスへの移行時に行うべきこと:

既存の 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() Remove
GoogleAuth.currentUser.listen() 削除
GoogleAuth.disconnect() google.accounts.oauth2.revoke 古いものを新しいものに置き換えます。https://myaccount.google.com/permissions から取り消すこともできます。
GoogleAuth.grantOfflineAccess() 削除するには、認可コードフローに沿って操作します。
GoogleAuth.isSignedIn.get() 削除
GoogleAuth.isSignedIn.listen() Remove
GoogleAuth.signIn() Remove
GoogleAuth.signOut() Remove
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.authorize() 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 要素の HTML DOM 読み込みまたは google.accounts.id.renderButton への JS 呼び出しにより、ユーザーが Google アカウントにログインします。

認証情報の例

既存の認証情報

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

access_token と id_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"
    }
  }