このドキュメントでは、JavaScript ウェブ アプリケーションから Google API にアクセスするための OAuth 2.0 認可を実装する方法について説明します。OAuth 2.0 では、ユーザー名やパスワードなどの情報を秘密にしたまま、ユーザーが特定のデータをアプリケーションと共有できます。たとえば、アプリケーションで OAuth 2.0 を使い、Google ドライブにファイルを保存する許可をユーザーから取得できます。
この OAuth 2.0 フローは、暗黙的グラント フローと呼ばれます。これは、ユーザーがアプリケーションにいる間のみ API にアクセスするアプリケーション向けに設計されています。これらのアプリケーションは機密情報を保存できません。
このフローでは、アプリはクエリ パラメータを使用してアプリとアプリに必要な API アクセスの種類を識別する Google URL を開きます。URL は現在のブラウザ ウィンドウまたはポップアップで開くことができます。ユーザーは Google で認証を行い、リクエストされた権限を付与できます。Google はユーザーをアプリにリダイレクトします。リダイレクトにはアクセス トークンが含まれており、アプリはこのトークンを検証してから API リクエストを行います。
Google API クライアント ライブラリと Google Identity Services
JavaScript 用 Google API クライアント ライブラリを使用して Google への承認済み呼び出しを行う場合は、 Google Identity Services JavaScript ライブラリを使用して OAuth 2.0 フローを処理する必要があります。Google Identity Services のトークンモデル(OAuth 2.0 のインプリシット グラント フローに基づく)をご覧ください。
前提条件
プロジェクトでAPI を有効にする
Google API を呼び出すアプリケーションは、 API Consoleでそれらの API を有効にする必要があります。
プロジェクトで API を有効にするには:
- Google API ConsoleのOpen the API Library 。
- If prompted, select a project, or create a new one.
- API Library には、利用可能なすべての API がプロダクト ファミリーと人気度に応じて分類されて表示されます。有効にする API がリストで見当たらない場合は、検索して見つけるか、その API が属するプロダクト ファミリーで [すべて表示] をクリックします。
- 有効にする API を選択し、[有効にする] ボタンをクリックします。
- If prompted, enable billing.
- If prompted, read and accept the API's Terms of Service.
承認認証情報を作成する
OAuth 2.0 を使用して Google API にアクセスするアプリケーションは、Google の OAuth 2.0 サーバーに対して自身の身元を示す認証情報を持つ必要があります。次の手順では、プロジェクトの認証情報を作成する方法について説明します。アプリケーションは、この認証情報を使用して、そのプロジェクトで有効にした API にアクセスできます。
- Go to the Credentials page.
- [Create Client] をクリックします。
- アプリケーションの種類として [ウェブ アプリケーション] を選択します。
- フォームに入力します。JavaScript を使用して承認済みの Google API リクエストを行うアプリケーションは、承認済みの JavaScript 生成元を指定する必要があります。オリジンは、アプリケーションが OAuth 2.0 サーバーにリクエストを送信できるドメインを識別します。これらのオリジンは、Google の検証ルールに準拠する必要があります。
アクセス スコープを特定する
スコープを指定すると、アプリケーションからのアクセス要求は必要なリソースのみに限定されるようになり、ユーザーはアプリケーションに付与するアクセスレベルを制御できます。したがって、リクエストされるスコープの数とユーザーの同意を得られる可能性の間には逆相関がある可能性があります。
OAuth 2.0 認証の実装を開始する前に、アプリがアクセス権限を必要とするスコープを設定しておくことをおすすめします。
OAuth 2.0 API スコープのドキュメントには、Google API へのアクセスに使用できるスコープの完全なリストが記載されています。
OAuth 2.0 アクセス トークンを取得する
次の手順は、ユーザーに代わって API リクエストを実行するためのユーザーの同意を得るために、アプリケーションが Google の OAuth 2.0 サーバーとやり取りする方法を示しています。ユーザーの承認が必要な Google API リクエストを実行するには、アプリがその同意を得ている必要があります。
ステップ 1: Google の OAuth 2.0 サーバーにリダイレクトする
ユーザーのデータにアクセスする権限をリクエストするには、ユーザーを Google の OAuth 2.0 サーバーにリダイレクトします。
OAuth 2.0 エンドポイント
https://accounts.google.com/o/oauth2/v2/auth
の Google の OAuth 2.0 エンドポイントからアクセスをリクエストする URL を生成します。このエンドポイントには HTTPS でアクセスできます。プレーン HTTP 接続は拒否されます。
Google 認可サーバーは、ウェブサーバー アプリケーションに対して次のクエリ文字列パラメータをサポートしています。
パラメータ | |||||||
---|---|---|---|---|---|---|---|
client_id |
必須
アプリケーションのクライアント ID。この値は、 にあります。 |
||||||
redirect_uri |
必須
ユーザーが認可フローを完了した後に API サーバーがユーザーをリダイレクトする場所を指定します。この値は、クライアントの
で構成した OAuth 2.0 クライアントの承認済みリダイレクト URI のいずれかと完全に一致している必要があります。この値が、指定された
|
||||||
response_type |
必須
JavaScript アプリケーションでは、パラメータの値を |
||||||
scope |
必須
アプリケーションがユーザーの代わりにアクセスできるリソースを識別するスコープのスペース区切りリスト。これらの値は、Google がユーザーに表示する同意画面に反映されます。 スコープを指定すると、アプリケーションからのアクセス要求は必要なリソースのみに限定されるようになり、ユーザーはアプリケーションに付与するアクセスレベルを制御できます。したがって、リクエストされるスコープの数とユーザーの同意が得られる可能性の間には逆相関があります。 可能な限り、コンテキストで認可スコープへのアクセスをリクエストすることをおすすめします。段階的認証を通じて、ユーザーデータへのアクセス権限を状況に合わせてリクエストすることで、ユーザーはアプリがリクエストしているアクセス権限を必要とする理由をより簡単に理解できます。 |
||||||
state |
推奨
認可リクエストと認可サーバーのレスポンス間で状態を維持するためにアプリケーションが使用する文字列値を指定します。ユーザーがアプリのアクセス リクエストを承認または拒否すると、サーバーは このパラメータは、ユーザーをアプリケーション内の正しいリソースに誘導する、ノンスを送信する、クロスサイト リクエスト フォージェリを軽減するなど、さまざまな目的で使用できます。 |
||||||
include_granted_scopes |
省略可 アプリケーションが段階的認可を使用して、コンテキスト内の追加のスコープへのアクセスをリクエストできるようにします。このパラメータの値を |
||||||
enable_granular_consent |
省略可 デフォルトは Google がアプリのきめ細かい権限を有効にすると、このパラメータは無効になります。 |
||||||
login_hint |
省略可 アプリが認証を試みているユーザーを把握している場合は、このパラメータを使用して Google 認証サーバーにヒントを提供できます。サーバーは、ヒントを使用して、ログイン フォームのメール フィールドに事前入力するか、適切なマルチログイン セッションを選択することで、ログイン フローを簡素化します。 パラメータ値を、ユーザーの Google ID に相当するメールアドレスまたは |
||||||
prompt |
省略可 ユーザーに表示するプロンプトのスペース区切りの大文字と小文字を区別するリスト。このパラメータを指定しない場合、ユーザーにプロンプトが表示されるのは、プロジェクトがアクセスをリクエストした最初の 1 回のみです。詳しくは、 再同意を求めるをご覧ください。 次の値があります。
|
Google の認可サーバーへのリダイレクトの例
読みやすくするため、改行とスペースを挿入した URL の例を以下に示します。
https://accounts.google.com/o/oauth2/v2/auth? scope=https%3A//www.googleapis.com/auth/drive.metadata.readonly%20https%3A//www.googleapis.com/auth/calendar.readonly& include_granted_scopes=true& response_type=token& state=state_parameter_passthrough_value& redirect_uri=https%3A//oauth2.example.com/code& client_id=client_id
リクエスト URL を作成したら、ユーザーをその URL にリダイレクトします。
JavaScript サンプルコード
次の JavaScript スニペットは、JavaScript 用 Google APIs クライアント ライブラリを使用せずに JavaScript で認証フローを開始する方法を示しています。この OAuth 2.0 エンドポイントはクロスオリジン リソース シェアリング(CORS)をサポートしていないため、このスニペットは、そのエンドポイントへのリクエストを開くフォームを作成します。
/* * 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_REDIRECT_URI', 'response_type': 'token', 'scope': 'https://www.googleapis.com/auth/drive.metadata.readonly https://www.googleapis.com/auth/calendar.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(); }
ステップ 2: Google がユーザーに同意を求める
このステップで、ユーザーはリクエストされたアクセス権をアプリケーションに付与するかどうかを決定します。この段階で、Google は同意ウィンドウを表示します。このウィンドウには、アプリケーションの名前と、ユーザーの認証情報を使用してアクセス権限をリクエストしている Google API サービス、付与されるアクセス スコープの概要が表示されます。ユーザーは、アプリがリクエストした 1 つ以上のスコープへのアクセス権の付与に同意するか、リクエストを拒否できます。
この段階では、アプリケーションは Google の OAuth 2.0 サーバーからのレスポンスを待機するだけで、何もする必要はありません。レスポンスには、アクセスが許可されたかどうかが示されます。このレスポンスについては、次のステップで説明します。
エラー
Google の OAuth 2.0 認可エンドポイントへのリクエストで、想定される認証フローと認可フローではなく、ユーザー向けのエラー メッセージが表示されることがあります。一般的なエラーコードと推奨される解決策を以下に示します。
admin_policy_enforced
Google Workspace 管理者のポリシーにより、リクエストされた 1 つ以上のスコープを Google アカウントが承認できません。管理者が OAuth クライアント ID に明示的にアクセス権を付与するまで、すべてのスコープまたは機密性の高い制限付きスコープへのアクセスを制限する方法について詳しくは、Google Workspace 管理者向けヘルプ記事の Google Workspace のデータにアクセスできるサードパーティ製アプリと内部アプリを制御するをご覧ください。
disallowed_useragent
認可エンドポイントは、Google の OAuth 2.0 ポリシーで禁止されている埋め込みユーザー エージェント内に表示されます。
Android
Android デベロッパーは、android.webkit.WebView
で承認リクエストを開くときに、このエラー メッセージが表示されることがあります。代わりに、デベロッパーは Google Sign-In for Android や OpenID Foundation の AppAuth for Android などの Android ライブラリを使用する必要があります。
このエラーは、Android アプリが埋め込みユーザー エージェントで一般的なウェブリンクを開き、ユーザーがサイトから Google の OAuth 2.0 認可エンドポイントに移動したときに、ウェブ デベロッパーに発生する可能性があります。デベロッパーは、Android アプリリンク ハンドラまたはデフォルトのブラウザアプリを含む、オペレーティング システムのデフォルトのリンク ハンドラで一般的なリンクを開くことを許可する必要があります。Android カスタムタブ ライブラリもサポートされているオプションです。
iOS
iOS と macOS のデベロッパーは、WKWebView
で承認リクエストを開くときにこのエラーが発生することがあります。代わりに、デベロッパーは Google Sign-In for iOS や OpenID Foundation の AppAuth for iOS などの iOS ライブラリを使用する必要があります。
ウェブ デベロッパーは、iOS または macOS アプリが埋め込みユーザー エージェントで一般的なウェブリンクを開き、ユーザーがサイトから Google の OAuth 2.0 認可エンドポイントに移動したときに、このエラーに遭遇する可能性があります。デベロッパーは、ユニバーサル リンク ハンドラまたはデフォルトのブラウザアプリを含む、オペレーティング システムのデフォルトのリンク ハンドラで一般的なリンクを開けるようにする必要があります。SFSafariViewController
ライブラリもサポートされているオプションです。
org_internal
リクエストの OAuth クライアント ID は、特定の Google Cloud 組織内の Google アカウントへのアクセスを制限するプロジェクトの一部です。この構成オプションの詳細については、OAuth 同意画面の設定に関するヘルプ記事のユーザータイプのセクションをご覧ください。
invalid_client
リクエストが送信されたオリジンは、このクライアントに対して承認されていません。origin_mismatch
をご覧ください。
deleted_client
リクエストの作成に使用されている OAuth クライアントが削除されました。削除は手動で行うことも、未使用のクライアント の場合は自動で行うこともできます。削除したクライアントは、削除後 30 日以内であれば復元できます。詳しくは、こちら をご覧ください。
invalid_grant
増分認可を使用している場合、トークンの有効期限が切れているか、無効になっている可能性があります。ユーザーを再度認証し、新しいトークンを取得するためのユーザーの同意を求めます。このエラーが引き続き表示される場合は、アプリケーションが正しく構成されていること、リクエストで正しいトークンとパラメータを使用していることを確認してください。それ以外の場合は、ユーザー アカウントが削除または無効になっている可能性があります。
origin_mismatch
承認リクエストの送信元である JavaScript のスキーム、ドメイン、ポートが、OAuth クライアント ID に登録されている承認済みの JavaScript 生成元 URI と一致していない可能性があります。 で承認済みの JavaScript 生成元を確認します。
redirect_uri_mismatch
認可リクエストで渡された redirect_uri
が、OAuth クライアント ID の承認済みリダイレクト URI と一致しません。
で承認済みのリダイレクト URI を確認します。
承認リクエストの送信元である JavaScript のスキーム、ドメイン、ポートが、OAuth クライアント ID に登録されている承認済みの JavaScript 生成元 URI と一致していない可能性があります。 で承認済みの JavaScript 生成元を確認します。
redirect_uri
パラメータは、非推奨となりサポートが終了した OAuth 帯域外(OOB)フローを参照している可能性があります。統合を更新するには、移行ガイドを参照してください。
invalid_request
リクエストに問題がありました。これには、次のような理由が考えられます。
- リクエストの形式が正しくありませんでした
- リクエストに必須パラメータが含まれていませんでした
- リクエストで、Google がサポートしていない認証方法が使用されています。OAuth 統合で推奨される統合方法が使用されていることを確認する
ステップ 3: OAuth 2.0 サーバーのレスポンスを処理する
OAuth 2.0 エンドポイント
OAuth 2.0 サーバーは、アクセス トークン リクエストで指定された redirect_uri
にレスポンスを送信します。
ユーザーがリクエストを承認すると、レスポンスにアクセス トークンが格納されます。ユーザーがリクエストを承認しないと、レスポンスにエラー メッセージが格納されます。アクセス トークンまたはエラー メッセージは、次のようにリダイレクト URI のハッシュ フラグメントで返されます。
アクセス トークン レスポンス:
https://oauth2.example.com/callback#access_token=4/P7q7W91&token_type=Bearer&expires_in=3600
フラグメント文字列には、
access_token
パラメータに加えて、常にBearer
に設定されるtoken_type
パラメータと、トークンの有効期間を秒単位で指定するexpires_in
パラメータも含まれます。アクセス トークン リクエストでstate
パラメータが指定されている場合、その値もレスポンスに含まれます。- エラー レスポンス:
https://oauth2.example.com/callback#error=access_denied
OAuth 2.0 サーバー レスポンスの例
このフローをテストするには、次のサンプル URL をクリックします。この URL は、Google ドライブのファイルのメタデータを表示するための読み取り専用アクセスと、Google カレンダーの予定を表示するための読み取り専用アクセスをリクエストします。
https://accounts.google.com/o/oauth2/v2/auth? scope=https%3A//www.googleapis.com/auth/drive.metadata.readonly%20https%3A//www.googleapis.com/auth/calendar.readonly& include_granted_scopes=true& response_type=token& state=state_parameter_passthrough_value& redirect_uri=https%3A//oauth2.example.com/code& client_id=client_id
OAuth 2.0 フローが完了すると、http://localhost/oauth2callback
にリダイレクトされます。ローカルマシンがそのアドレスでファイルを配信していない限り、その URL は 404 NOT FOUND
エラーを返します。次のステップでは、ユーザーがアプリケーションにリダイレクトされたときに URI で返される情報について詳しく説明します。
ステップ 4: ユーザーが許可したスコープを確認する
複数の権限(スコープ)をリクエストする場合、ユーザーがアプリにすべての権限へのアクセスを許可するとは限りません。アプリは、実際に付与されたスコープを確認し、一部の権限が拒否された状況に適切に対応する必要があります。通常は、拒否されたスコープに依存する機能を無効にします。
ただし、例外もあります。ドメイン全体の権限の委任が設定されている Google Workspace Enterprise アプリ、または信頼できるアプリとしてマークされているアプリは、詳細な権限の同意画面をスキップします。これらのアプリでは、ユーザーにきめ細かい権限の同意画面は表示されません。代わりに、アプリはリクエストされたすべてのスコープを受け取るか、1 つも受け取らないかのどちらかになります。
詳しくは、きめ細かい権限を処理する方法をご覧ください。
OAuth 2.0 エンドポイント
ユーザーが特定のスコープへのアクセス権をアプリケーションに付与しているかどうかを確認するには、アクセス トークン レスポンスの scope
フィールドを調べます。access_token によって付与されたアクセス権のスコープ。スペース区切りの大文字と小文字が区別される文字列のリストとして表されます。
たとえば、次のサンプル アクセス トークン レスポンスは、ユーザーが読み取り専用のドライブ アクティビティとカレンダーの予定の権限をアプリに付与したことを示しています。
{ "access_token": "1/fFAGRNJru1FTz70BzhT3Zg", "expires_in": 3920, "token_type": "Bearer", "scope": "https://www.googleapis.com/auth/drive.metadata.readonly https://www.googleapis.com/auth/calendar.readonly", "refresh_token": "1//xEoDL4iW3cxlI7yDbSRFYNG01kVKM2C-259HOF2aQbI" }
Google API の呼び出し
OAuth 2.0 エンドポイント
アプリケーションがアクセス トークンを取得した後、API で必要なアクセス スコープが付与されている場合は、トークンを使用して特定のユーザー アカウントの代わりに Google API を呼び出すことができます。これを行うには、access_token
クエリ パラメータまたは Authorization
HTTP ヘッダー Bearer
値のいずれかを含めることで、API へのリクエストにアクセス トークンを含めます。クエリ文字列はサーバーログに表示される傾向があるため、可能な場合は HTTP ヘッダーを使用することをおすすめします。ほとんどの場合、クライアント ライブラリを使用して Google API への呼び出しを設定できます(たとえば、Drive Files API を呼び出す場合など)。
OAuth 2.0 Playground では、すべての Google API を試して、そのスコープを確認できます。
HTTP GET の例
Authorization: Bearer
HTTP ヘッダーを使用して
drive.files
エンドポイント(ドライブ ファイル API)を呼び出すと、次のようになります。独自のアクセス トークンを指定する必要があります。
GET /drive/v2/files HTTP/1.1 Host: www.googleapis.com Authorization: Bearer access_token
以下は、access_token
クエリ文字列パラメータを使用して、認証済みユーザーに対して同じ API を呼び出す例です。
GET https://www.googleapis.com/drive/v2/files?access_token=access_token
curl
の例
これらのコマンドは、curl
コマンドライン アプリケーションでテストできます。HTTP ヘッダー オプション(推奨)を使用する例を次に示します。
curl -H "Authorization: Bearer access_token" https://www.googleapis.com/drive/v2/files
または、クエリ文字列パラメータ オプションを使用します。
curl https://www.googleapis.com/drive/v2/files?access_token=access_token
JavaScript サンプルコード
次のコード スニペットは、CORS(クロスオリジン リソース シェアリング)を使用して Google API にリクエストを送信する方法を示しています。この例では、JavaScript 用 Google API クライアント ライブラリを使用していません。ただし、クライアント ライブラリを使用していない場合でも、そのライブラリのドキュメントにある CORS サポートのガイドは、これらのリクエストをより深く理解するのに役立つ可能性があります。
このコード スニペットでは、access_token
変数は、承認済みユーザーに代わって API リクエストを行うために取得したトークンを表します。完全な例では、そのトークンをブラウザのローカル ストレージに保存し、API リクエストを行うときに取得する方法を示します。
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) { console.log(xhr.response); }; xhr.send(null);
サンプルコードの全文
OAuth 2.0 エンドポイント
このコードサンプルは、JavaScript 用 Google API クライアント ライブラリを使用せずに JavaScript で OAuth 2.0 フローを完了する方法を示しています。このコードは、API リクエストを試すためのボタンを表示する HTML ページ用です。ボタンをクリックすると、ページがブラウザのローカル ストレージに API アクセス トークンを保存しているかどうかがコードによって確認されます。条件を満たしている場合、API リクエストが実行されます。それ以外の場合は、OAuth 2.0 フローを開始します。
OAuth 2.0 フローの場合、ページは次の手順を行います。
- ユーザーは Google の OAuth 2.0 サーバーにリダイレクトされ、
https://www.googleapis.com/auth/drive.metadata.readonly
スコープとhttps://www.googleapis.com/auth/calendar.readonly
スコープへのアクセスがリクエストされます。 - 1 つ以上のリクエストされたスコープへのアクセスを許可(または拒否)すると、ユーザーは元のページにリダイレクトされます。元のページでは、フラグメント識別子文字列からアクセス トークンが解析されます。
- このページでは、ユーザーがアプリケーションへのアクセスを許可したスコープを確認できます。
ユーザーがリクエストされた scope() へのアクセスを許可した場合、ページはアクセス トークンを使用してサンプル API リクエストを行います。
API リクエストは、Drive API の
about.get
メソッドを呼び出して、承認済みユーザーの Google ドライブ アカウントに関する情報を取得します。- リクエストが正常に実行されると、API レスポンスがブラウザのデバッグ コンソールに記録されます。
Google アカウントの [権限] ページで、アプリへのアクセスを取り消すことができます。アプリは [OAuth 2.0 Demo for Google API Docs] として表示されます。
このコードをローカルで実行するには、認証情報に対応する YOUR_CLIENT_ID
変数と YOUR_REDIRECT_URI
変数の値を設定する必要があります。YOUR_REDIRECT_URI
変数は、ページが配信されている URL と同じ URL に設定する必要があります。この値は、
で構成した OAuth 2.0 クライアントの承認済みリダイレクト URI のいずれかと完全に一致する必要があります。この値が承認済みの URI と一致しない場合、redirect_uri_mismatch
エラーが発生します。また、プロジェクトでこのリクエストの適切な API を有効にする必要があります。
<html><head></head><body> <script> var YOUR_CLIENT_ID = 'REPLACE_THIS_VALUE'; var YOUR_REDIRECT_URI = 'REPLACE_THIS_VALUE'; // Parse query string to see if page request is coming from OAuth 2.0 server. var fragmentString = location.hash.substring(1); var params = {}; var regex = /([^&=]+)=([^&]*)/g, m; while (m = regex.exec(fragmentString)) { params[decodeURIComponent(m[1])] = decodeURIComponent(m[2]); } if (Object.keys(params).length > 0 && params['state']) { if (params['state'] == localStorage.getItem('state')) { localStorage.setItem('oauth2-test-params', JSON.stringify(params) ); trySampleRequest(); } else { console.log('State mismatch. Possible CSRF attack'); } } // Function to generate a random state value function generateCryptoRandomState() { const randomValues = new Uint32Array(2); window.crypto.getRandomValues(randomValues); // Encode as UTF-8 const utf8Encoder = new TextEncoder(); const utf8Array = utf8Encoder.encode( String.fromCharCode.apply(null, randomValues) ); // Base64 encode the UTF-8 data return btoa(String.fromCharCode.apply(null, utf8Array)) .replace(/\+/g, '-') .replace(/\//g, '_') .replace(/=+$/, ''); } // 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']) { // User authorized the request. Now, check which scopes were granted. if (params['scope'].includes('https://www.googleapis.com/auth/drive.metadata.readonly')) { // User authorized read-only Drive activity permission. // Calling the APIs, etc. 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 { // User didn't authorize read-only Drive activity permission. // Update UX and application accordingly console.log('User did not authorize read-only Drive activity permission.'); } // Check if user authorized Calendar read permission. if (params['scope'].includes('https://www.googleapis.com/auth/calendar.readonly')) { // User authorized Calendar read permission. // Calling the APIs, etc. console.log('User authorized Calendar read permission.'); } else { // User didn't authorize Calendar read permission. // Update UX and application accordingly console.log('User did not authorize Calendar read permission.'); } } else { oauth2SignIn(); } } /* * Create form to request access token from Google's OAuth 2.0 server. */ function oauth2SignIn() { // create random state value and store in local storage var state = generateCryptoRandomState(); localStorage.setItem('state', state); // 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 https://www.googleapis.com/auth/calendar.readonly', 'state': state, '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>
JavaScript 生成元検証ルール
Google は、デベロッパーがアプリケーションの安全性を維持できるように、JavaScript のオリジンに次の検証ルールを適用します。JavaScript の生成元は、次のルールに準拠している必要があります。以下で説明するドメイン、ホスト、スキームの定義については、RFC 3986 セクション 3 をご覧ください。
検証規則 | |
---|---|
スキーム |
JavaScript のオリジンでは、プレーン HTTP ではなく HTTPS スキームを使用する必要があります。ローカルホスト URI(ローカルホスト IP アドレス URI を含む)は、このルールの対象外です。 |
ホスト |
ホストに未加工の IP アドレスを指定することはできません。ローカルホストの IP アドレスはこのルールの対象外です。 |
ドメイン |
“googleusercontent.com” にすることはできません。goo.gl など)を含めることはできません。 |
Userinfo |
JavaScript のオリジンに userinfo サブコンポーネントを含めることはできません。 |
[Path] |
JavaScript のオリジンにパス コンポーネントを含めることはできません。 |
クエリ |
JavaScript のオリジンにクエリ コンポーネントを含めることはできません。 |
Fragment |
JavaScript のオリジンにフラグメント コンポーネントを含めることはできません。 |
文字数 |
JavaScript 生成元には、次の文字を含めることはできません。
|
段階的な認可
OAuth 2.0 プロトコルでは、アプリはリソースへのアクセス権限をリクエストします。リソースはスコープによって識別されます。リソースの承認は、必要なときにリクエストすることがユーザー エクスペリエンスのベスト プラクティスとされています。この方法を有効にするため、Google の認可サーバーは段階的な認可をサポートしています。この機能を使用すると、必要なときにスコープをリクエストできます。ユーザーが新しいスコープの権限を付与すると、ユーザーがプロジェクトに付与したすべてのスコープを含むトークンと交換できる認証コードが返されます。
たとえば、音楽トラックを試聴してミックスを作成できるアプリでは、ログイン時に必要なリソースはごくわずかかもしれません。ログインするユーザーの名前だけが必要な場合もあります。ただし、完成したミックスを保存するには、そのユーザーの Google ドライブにアクセスする必要があります。ほとんどのユーザーは、アプリが実際に Google ドライブへのアクセスを必要とするタイミングでのみアクセス権限を求められることを自然に感じます。
この場合、アプリはログイン時に openid
スコープと profile
スコープをリクエストして基本的なログインを行い、その後、最初のミックス保存リクエスト時に https://www.googleapis.com/auth/drive.file
スコープをリクエストする可能性があります。
増分認可で取得したアクセス トークンには、次のルールが適用されます。
- このトークンを使用して、新しい統合認可にロールアップされたスコープに対応するリソースにアクセスできます。
- 結合された承認の更新トークンを使用してアクセス トークンを取得すると、アクセス トークンは結合された承認を表し、レスポンスに含まれる
scope
値のいずれにも使用できます。 - 統合された認可には、ユーザーが API プロジェクトに付与したすべてのスコープが含まれます。付与が異なるクライアントからリクエストされた場合でも同様です。たとえば、ユーザーがアプリケーションのデスクトップ クライアントを使用して 1 つのスコープへのアクセスを許可し、モバイル クライアントを使用して同じアプリケーションに別のスコープを許可した場合、統合された認可には両方のスコープが含まれます。
- 結合された認可を表すトークンを取り消すと、関連付けられたユーザーに代わって、その認可のすべてのスコープへのアクセスが同時に取り消されます。
次のコードサンプルは、既存のアクセス トークンにスコープを追加する方法を示しています。このアプローチにより、アプリは複数のアクセス トークンを管理する必要がなくなります。
OAuth 2.0 エンドポイント
既存のアクセス トークンにスコープを追加するには、Google の OAuth 2.0 サーバーへのリクエストに include_granted_scopes
パラメータを含めます。
次のコード スニペットは、その方法を示しています。このスニペットは、アクセス トークンが有効なスコープをブラウザのローカル ストレージに保存していることを前提としています。(完全なサンプルコードでは、ブラウザのローカル ストレージで oauth2-test-params.scope
プロパティを設定して、アクセス トークンが有効なスコープのリストを保存します)。
このスニペットは、アクセス トークンが有効なスコープと、特定のクエリで使用するスコープを比較します。アクセス トークンがそのスコープをカバーしていない場合、OAuth 2.0 フローが開始されます。ここで、oauth2SignIn
関数は、ステップ 2 で提供された関数と同じです(後で完全な例で提供されます)。
var SCOPE = 'https://www.googleapis.com/auth/drive.metadata.readonly'; var params = JSON.parse(localStorage.getItem('oauth2-test-params')); var current_scope_granted = false; if (params.hasOwnProperty('scope')) { var scopes = params['scope'].split(' '); for (var s = 0; s < scopes.length; s++) { if (SCOPE == scopes[s]) { current_scope_granted = true; } } } if (!current_scope_granted) { oauth2SignIn(); // This function is defined elsewhere in this document. } else { // Since you already have access, you can proceed with the API request. }
トークンの取り消し
ユーザーがアプリに付与したアクセス権を取り消したい場合もあります。ユーザーは、 アカウント設定にアクセスしてアクセス権を取り消すことができます。詳しくは、アカウントにアクセスできるサードパーティのサイトやアプリのサポート ドキュメントの「サイトやアプリのアクセス権を削除する」セクションをご覧ください。
アプリケーションが、自身に付与されたアクセス権をプログラムで取り消すことも可能です。プログラムによる取り消しは、ユーザーが登録を解除した場合、アプリを削除した場合、アプリに必要な API リソースが大幅に変更された場合などに重要です。つまり、削除プロセスの一部として、以前にアプリに付与された権限が削除されるように API リクエストを含めることができます。
OAuth 2.0 エンドポイント
プログラムでトークンを取り消すには、アプリケーションが https://oauth2.googleapis.com/revoke
にリクエストを送信し、トークンをパラメータとして含めます。
curl -d -X -POST --header "Content-type:application/x-www-form-urlencoded" \ https://oauth2.googleapis.com/revoke?token={token}
トークンはアクセス トークンまたは更新トークンです。トークンがアクセス トークンで、対応する更新トークンがある場合、更新トークンも取り消されます。
取り消しが正常に処理されると、レスポンスの HTTP ステータス コードは 200
になります。エラー条件の場合、エラーコードとともに HTTP ステータス コード 400
が返されます。
次の JavaScript スニペットは、JavaScript 用 Google API クライアント ライブラリを使用せずに JavaScript でトークンを取り消す方法を示しています。トークンを取り消すための Google の OAuth 2.0 エンドポイントはクロスオリジン リソース シェアリング(CORS)をサポートしていないため、コードは XMLHttpRequest()
メソッドを使用してリクエストを投稿するのではなく、フォームを作成してエンドポイントに送信します。
function revokeAccess(accessToken) { // Google's OAuth 2.0 endpoint for revoking access tokens. var revokeTokenEndpoint = 'https://oauth2.googleapis.com/revoke'; // Create <form> element to use to POST data to the OAuth 2.0 endpoint. var form = document.createElement('form'); form.setAttribute('method', 'post'); form.setAttribute('action', revokeTokenEndpoint); // Add access token to the form so it is set as value of 'token' parameter. // This corresponds to the sample curl request, where the URL is: // https://oauth2.googleapis.com/revoke?token={token} var tokenField = document.createElement('input'); tokenField.setAttribute('type', 'hidden'); tokenField.setAttribute('name', 'token'); tokenField.setAttribute('value', accessToken); form.appendChild(tokenField); // Add form to page and submit it to actually revoke the token. document.body.appendChild(form); form.submit(); }
クロスアカウント保護機能の実装
ユーザーのアカウントを保護するために行うべき追加の手順として、Google のクロス アカウント保護サービスを利用してクロス アカウント保護を実装することが挙げられます。このサービスでは、ユーザー アカウントの大きな変更に関する情報をアプリケーションに提供するセキュリティ イベント通知を登録できます。この情報を使用して、イベントへの対応方法に応じてアクションを実行できます。
Google のクロス アカウント保護サービスからアプリに送信されるイベントタイプの例を次に示します。
-
https://schemas.openid.net/secevent/risc/event-type/sessions-revoked
-
https://schemas.openid.net/secevent/oauth/event-type/token-revoked
-
https://schemas.openid.net/secevent/risc/event-type/account-disabled
クロスアカウント保護の実装方法と利用可能なイベントの完全なリストについては、 クロスアカウント保護でユーザー アカウントを保護する をご覧ください。