概要
Google API を呼び出すためのユーザーごとのアクセス トークンを取得するために、Google は複数の JavaScript ライブラリを提供しています。
このガイドでは、これらのライブラリから Google Identity Services ライブラリに移行する手順について説明します。
このガイドでは、次のことが可能になります。
- 非推奨のプラットフォーム ライブラリを Identity Services ライブラリに置き換える
- API クライアント ライブラリを使用している場合は、非推奨の
gapi.auth2
モジュール、そのメソッド、オブジェクトを削除し、Identity Services の同等のものに置き換えます。
Identity Services JavaScript ライブラリの変更点については、概要とユーザー認可の仕組みを参照して、主な用語とコンセプトを確認してください。
ユーザーの登録とログインの認証については、Google ログインからの移行をご覧ください。
承認フローを特定する
ユーザー認可フローには、暗黙的認可と認証コードの 2 つがあります。
ウェブアプリを確認して、現在使用されている承認フローのタイプを特定します。
ウェブアプリが暗黙的フローを使用していることを示す兆候:
- ウェブアプリが純粋にブラウザベースで、バックエンド プラットフォームがない。
- Google API を呼び出すにはユーザーが存在している必要があります。アプリはアクセス トークンのみを使用しており、更新トークンは必要ありません。
- ウェブアプリが
apis.google.com/js/api.js
を読み込みます。 - 実装は クライアントサイド ウェブ アプリケーション用の OAuth 2.0 に基づいています。
- アプリで、JavaScript 用 Google API クライアント ライブラリにある
gapi.client
モジュールまたはgapi.auth2
モジュールを使用している。
ウェブアプリが認可コードフローを使用していることを示す兆候:
実装は次のとおりです。
アプリは、ユーザーのブラウザとバックエンド プラットフォームの両方で実行されます。
バックエンド プラットフォームが認証コード エンドポイントをホストしている。
バックエンド プラットフォームが、ユーザーの存在を必要とせずにユーザーに代わって 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 アカウントのイベントとログイン ステータス変更のリスナー。
ウェブアプリへのログイン状態とユーザー セッションの管理は、デベロッパーの責任となります。
次の 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
。
認可コードフロー
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 トークン フローをトリガーします。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 アカウントのログイン プロセスが開始されます。
- 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()
に置き換えます。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 <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_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" } }