概要
Google API を呼び出すユーザーごとのアクセス トークンを取得するために、Google は複数の JavaScript ライブラリを提供しています。
このガイドでは、これらのライブラリから Google Identity Services ライブラリに移行する手順について説明します。
このガイドに沿って作業すると、次のことができるようになります。
- 非推奨の Platform Library を 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 の使用は必要ありません。
ユーザー認証で 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
。
認可コードフロー
コード クライアントを初期化するの例に沿って、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 クライアント ライブラリ
ユーザーの同意を得るためのポップアップ ダイアログを使用してブラウザで実行される Google API Client Library for JavaScript の例。
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 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>
<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 呼び出しは、有効なアクセス トークンが返された後、コールバック内から行われます。
ユーザーは、ページが最初に読み込まれたときと、カレンダー情報を更新したいときに、[カレンダーを表示] ボタンを押すことが想定されています。
<!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 Sign-In 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 OAuth 2.0 エンドポイントへの直接呼び出しは、1 つのレスポンスで OAuth 2.0 アクセス トークンと OpenID Connect ID トークンの両方を返します。
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"
}
}