クライアントサイド ウェブ アプリケーション用の OAuth 2.0

コレクションでコンテンツを整理 必要に応じて、コンテンツの保存と分類を行います。

このドキュメントでは、JavaScript ウェブ アプリケーションから Google API にアクセスするための OAuth 2.0 認証を実装する方法について説明します。OAuth 2.0 を使用すると、ユーザー名やパスワードなどの情報を非公開にしたまま、特定のデータをアプリケーションと共有できます。たとえば、あるアプリケーションでは、OAuth 2.0 を使用して、ユーザーから Google ドライブにファイルを保存する権限を取得できます。

この OAuth 2.0 フローは「暗黙的な権限付与フロー」と呼ばれます。これは、ユーザーがアプリケーション内にいる間に API のみにアクセスするアプリケーション向けに設計されています。これらのアプリケーションは機密情報を保存できません。

このフローでは、クエリ パラメータを使用してアプリを特定し、アプリが必要とする API アクセスの種類を指定する Google URL が開きます。URL は、現在のブラウザ ウィンドウまたはポップアップで開きます。ユーザーは Google で認証を行い、リクエストされた権限を付与できます。 Google はユーザーをアプリにリダイレクトします。リダイレクトにはアクセス トークンが含まれます。アプリはこのアクセス トークンを検証し、それを使用して API リクエストを行います。

Prerequisites

プロジェクトでAPI を有効にする

Google API を呼び出すアプリケーションでは、 API Consoleで API を有効にする必要があります。

プロジェクトで API を有効にするには:

  1. Google API ConsoleのOpen the API Library
  2. If prompted, select a project, or create a new one.
  3. API Library には、利用可能なすべての API がプロダクト ファミリーと人気度に応じて分類されて表示されます。有効にしたい API がリストで見当たらない場合は、検索してその API を探すか、属しているプロダクト ファミリーの [すべて表示] をクリックします。
  4. 有効にする API を選択し、[有効にする] ボタンをクリックします。
  5. If prompted, enable billing.
  6. If prompted, read and accept the API's Terms of Service.

承認認証情報を作成する

OAuth 2.0 を使用して Google API にアクセスするアプリケーションには、Google の OAuth 2.0 サーバーに対してアプリケーションを識別するための認証情報が必要です。次の手順では、プロジェクトの認証情報を作成する方法について説明します。アプリケーションはその認証情報を使用して、そのプロジェクトで有効にした API にアクセスできます。

  1. Go to the Credentials page.
  2. [認証情報を作成] > [OAuth クライアント ID] をクリックします。
  3. アプリケーションの種類として [ウェブ アプリケーション] を選択します。
  4. フォームに入力します。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: クライアント オブジェクトを構成する

JavaScript の Google API クライアント ライブラリを使用して OAuth 2.0 フローを処理する場合は、まず gapi.auth2 オブジェクトと gapi.client オブジェクトを設定します。これらのオブジェクトを使用すると、アプリケーションでユーザー承認を取得し、承認済みの API リクエストを行うことができます。

クライアント オブジェクトは、アプリケーションがアクセス権限をリクエストしているスコープを識別します。これらの値により、Google がユーザーに表示する同意画面が表示されます。

JS クライアント ライブラリ

JavaScript クライアント ライブラリは、承認プロセスのさまざまな面を簡素化します。

  1. Google の承認サーバーのリダイレクト URL を作成し、その URL にユーザーを誘導する方法を提供します。
  2. そのサーバーからアプリケーションへのリダイレクトを処理します。
  3. 承認サーバーから返されたアクセス トークンを検証します。
  4. 認可サーバーがアプリケーションに送信するアクセス トークンを保存し、その後アプリが認証 API 呼び出しを行ったときに取得します。

以下のコード スニペットは、このドキュメントで後述する完全な例からの抜粋です。このコードは、後でアプリケーションで API 呼び出しを行うために使用する gapi.client オブジェクトを初期化します。そのオブジェクトが作成されると、アプリケーションでユーザーの承認ステータスの確認とモニタリングに使用される gapi.auth2 オブジェクトも初期化されます。

gapi.client.init の呼び出しでは、次のフィールドを指定します。

  • apiKeyclientId の値は、アプリケーションの認証情報を指定します。これらの値は、 認証情報の作成セクションで説明したように、 API Consoleで取得できます。アプリケーションで承認済みの API リクエストを行う場合は、clientId が必要です。未承認のリクエストのみを行うアプリケーションは、API キーを指定するだけで済みます。
  • scope フィールドには、アプリケーションがユーザーに代わってアクセスできるリソースに対応するアクセス スコープのスペース区切りリストを指定します。これらの値により、Google がユーザーに表示する同意画面が決まります。

    可能であれば、アプリケーションは状況に応じた認可スコープへのアクセスをリクエストすることをおすすめします。状況に応じてユーザーデータへのアクセスをリクエストすることで、 段階的な認可によって、アプリケーションがリクエストするアクセス権が必要な理由をユーザーが簡単に理解できるようになります。

  • discoveryDocs フィールドは、アプリケーションが使用する API Discovery ドキュメントのリストを識別します。Discovery ドキュメントには API のサーフェス(リソース スキーマなど)が記述され、JavaScript クライアント ライブラリはその情報を使用してアプリケーションで使用できるメソッドを生成します。この例のコードは、Google Drive API バージョン 3 のディスカバリ ドキュメントを取得します。

gapi.client.init 呼び出しが完了すると、Google Auth オブジェクトを識別するように GoogleAuth 変数が設定されます。最後に、ユーザーのログイン ステータスが変更されたときに関数を呼び出すリスナーをコードで設定します。(この関数はスニペットで定義されていません)。

var GoogleAuth; // Google Auth object.
function initClient() {
  gapi.client.init({
      'apiKey': 'YOUR_API_KEY',
      'clientId': 'YOUR_CLIENT_ID',
      'scope': 'https://www.googleapis.com/auth/drive.metadata.readonly',
      'discoveryDocs': ['https://www.googleapis.com/discovery/v1/apis/drive/v3/rest']
  }).then(function () {
      GoogleAuth = gapi.auth2.getAuthInstance();

      // Listen for sign-in state changes.
      GoogleAuth.isSignedIn.listen(updateSigninStatus);
  });
}

OAuth 2.0 エンドポイント

OAuth 2.0 エンドポイントに直接アクセスしている場合は、次のステップに進みます。

ステップ 2: Google の OAuth 2.0 サーバーにリダイレクトする

ユーザーのデータにアクセスする権限をリクエストするには、ユーザーを Google の OAuth 2.0 サーバーにリダイレクトします。

JS クライアント ライブラリ

GoogleAuth.signIn() メソッドを呼び出して、Google の承認サーバーにユーザーを誘導します。

GoogleAuth.signIn();

実際には、API 呼び出しの前に signIn() メソッドを呼び出すかどうかを決めるブール値を設定することがあります。

次のコード スニペットは、ユーザー認証フローを開始する方法を示しています。スニペットについては、以下の点に注意してください。

  • コードで参照される GoogleAuth オブジェクトは、ステップ 1 のコード スニペットで定義されているグローバル変数と同じです。

  • updateSigninStatus 関数は、ユーザーの承認ステータスの変更をリッスンするリスナーです。リスナーとしての役割も、ステップ 1 のコード スニペットで定義されています。
    GoogleAuth.isSignedIn.listen(updateSigninStatus);
  • 上のスニペットでは、さらに 2 つのグローバル変数を定義しています。

    • isAuthorized は、ユーザーがすでにログインしているかどうかを示すブール値変数です。この値は、ユーザーがアプリの読み込み時またはログアウト時に、アプリの読み込み時と更新時に設定できます。

      上のスニペットでは、sendAuthorizedApiRequest 関数が変数の値をチェックし、承認を必要とする API リクエストをアプリが試行するか、ユーザーにアプリの承認を求めるかを判定します。

    • currentApiRequest は、ユーザーが最後に行った API リクエストの詳細を格納するオブジェクトです。オブジェクトの値は、アプリが sendAuthorizedApiRequest 関数を呼び出すときに設定されます。

      ユーザーがアプリを承認した場合、リクエストはすぐに実行されます。それ以外の場合、関数はユーザーをログインにリダイレクトします。ユーザーがログインすると、updateSignInStatus 関数は sendAuthorizedApiRequest を呼び出し、認証フローが開始する前に試みられたのと同じリクエストを渡します。

var isAuthorized;
var currentApiRequest;

/**
 * Store the request details. Then check to determine whether the user
 * has authorized the application.
 *   - If the user has granted access, make the API request.
 *   - If the user has not granted access, initiate the sign-in flow.
 */
function sendAuthorizedApiRequest(requestDetails) {
  currentApiRequest = requestDetails;
  if (isAuthorized) {
    // Make API request
    // gapi.client.request(requestDetails)

    // Reset currentApiRequest variable.
    currentApiRequest = {};
  } else {
    GoogleAuth.signIn();
  }
}

/**
 * Listener called when user completes auth flow. If the currentApiRequest
 * variable is set, then the user was prompted to authorize the application
 * before the request executed. In that case, proceed with that API request.
 */
function updateSigninStatus(isSignedIn) {
  if (isSignedIn) {
    isAuthorized = true;
    if (currentApiRequest) {
      sendAuthorizedApiRequest(currentApiRequest);
    }
  } else {
    isAuthorized = false;
  }
}

OAuth 2.0 エンドポイント

https://accounts.google.com/o/oauth2/v2/auth で、Google の OAuth 2.0 エンドポイントからのアクセスをリクエストする URL を生成します。このエンドポイントは HTTPS 経由でアクセスできます。プレーン HTTP 接続は拒否されます。

Google の認可サーバーは、ウェブサーバー アプリケーションで次のクエリ文字列パラメータをサポートしています。

パラメータ
client_id 必須

アプリケーションのクライアント ID。この値は、 API Console Credentials pageで確認できます。

redirect_uri 必須

ユーザーが承認フローを完了した後に API サーバーがユーザーをリダイレクトする場所を決定します。この値は、クライアントの API Console Credentials pageで設定した OAuth 2.0 クライアントの承認済みのリダイレクト URI のいずれかと完全に一致する必要があります。この値が、指定された client_id の承認済みリダイレクト URI と一致しない場合は、redirect_uri_mismatch エラーが発生します。

http または https スキーム、ケース、末尾のスラッシュ(「/」)はすべて一致する必要があります。

response_type 必須

JavaScript アプリケーションでは、パラメータの値を token に設定する必要があります。この値は、承認プロセスの完了後にユーザーがリダイレクトされる URI(#)のフラグメント識別子で name=value ペアとしてアクセス トークンを返すように Google 認可サーバーに指示するものです。

scope 必須

アプリケーションがユーザーに代わってアクセスできるリソースを識別するためのスコープのスペース区切りリスト。これらの値により、Google がユーザーに表示する同意画面が表示されます。

スコープを使用すると、アプリケーションは必要なリソースへのアクセス権のみをリクエストできます。また、ユーザーはアプリケーションに付与するアクセス権の量を制御できます。したがって、要求されるスコープの数とユーザーの同意を取得する可能性の間には、逆の関係があります。

可能な限り、アプリケーションが状況に応じた認可スコープへのアクセスをリクエストすることをおすすめします。状況に応じてユーザーデータへのアクセスをリクエストすることで、段階的な認可によって、アプリケーションがリクエストするアクセス権を必要とする理由をユーザーが簡単に理解できるようになります。

state 推奨

アプリケーションが承認リクエストと承認サーバーのレスポンスの間で状態を維持するために使用する文字列値を指定します。ユーザーがアプリのアクセス リクエストを同意または拒否した後、redirect_uri の URL フラグメント識別子(#)で name=value ペアとして送信した値をそのままサーバーから返します。

このパラメータは、アプリ内の適切なリソースにユーザーを誘導する、ノンスを送信する、クロスサイト リクエスト フォージェリを軽減するなどの目的で使用できます。redirect_uri は推測できるため、state 値を使用すると、受信接続が認証リクエストの結果であるという保証を強化できます。ランダムな文字列を生成するか、クライアントの状態をキャプチャする Cookie または別の値のハッシュをエンコードする場合は、レスポンスを検証することで、リクエストとレスポンスが同じブラウザで発生したものでないことを確認できます。これにより、クロスサイト リクエスト フォージェリなどの攻撃を防ぐことができます。state トークンを作成して確認する方法の例については、OpenID Connect のドキュメントをご覧ください。

include_granted_scopes 省略可

アプリケーションが増分認可を使用して、コンテキスト内の追加スコープへのアクセスをリクエストできるようにします。このパラメータの値を true に設定し、承認リクエストが許可されると、ユーザーが以前にアプリケーションへのアクセス権を付与したスコープも新しいアクセス トークンでカバーされます。例については、段階的な認可をご覧ください。

login_hint 省略可

アプリケーションが認証しようとしているユーザーを認識している場合、このパラメータを使用して Google 認証サーバーにヒントを提供できます。サーバーはこのヒントを使用して、ログイン フォームにメールのフィールドに入力するか、適切なマルチログイン セッションを選択することで、ログインフローを簡素化します。

パラメータ値をメールアドレスまたは sub ID に設定します。これは、ユーザーの Google ID と同じです。

prompt 省略可

ユーザーに提示するプロンプトのスペース区切りリスト。大文字と小文字が区別されます。このパラメータを指定しない場合、プロジェクトが初めてアクセスをリクエストしたときにのみプロンプトが表示されます。詳しくは、 再同意のプロンプトをご覧ください。

表示される値は次のとおりです。

none 認証画面や同意画面を表示しません。他の値と一緒に指定することはできません。
consent 同意を求めるプロンプトをユーザーに表示する
select_account アカウントを選択するよう求められます。

Google の承認サーバーにリダイレクトする例

読みやすくするため、改行とスペースを含む URL の例を示します。

https://accounts.google.com/o/oauth2/v2/auth?
 scope=https%3A//www.googleapis.com/auth/drive.metadata.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 API クライアント ライブラリを使用せずに 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',
                '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();
}

ステップ 3: 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 で承認リクエストを開くと、このエラー メッセージを受け取ることがあります。代わりに、Android 向け Google ログインや OpenID Foundation の AppAuth for Android などの Android ライブラリを使用する必要があります。

このエラーは、Android アプリが埋め込みのユーザー エージェントで一般的なウェブリンクを開き、ユーザーがサイトから Google の OAuth 2.0 認証エンドポイントに移動したときに発生することがあります。デベロッパーは、Android App Links ハンドラまたはデフォルトのブラウザアプリの両方を含む、オペレーティング システムのデフォルトのリンク ハンドラで一般的なリンクを開くことができるようにする必要があります。Android カスタムタブ ライブラリもサポートされています。

iOS

iOS と macOS のデベロッパーは、WKWebView で承認リクエストを開いたときにこのエラーが発生することがあります。代わりに、iOS 向け Google ログインや OpenID Foundation の iOS 向け AppAuth などの iOS ライブラリを使用する必要があります。

このエラーは、iOS アプリまたは macOS アプリが埋め込みユーザー エージェントで一般的なウェブリンクを開き、ユーザーがサイトから Google の OAuth 2.0 認証エンドポイントに移動したときに発生することがあります。デベロッパーは、Universal Links ハンドラまたはデフォルトのブラウザアプリの両方を含む、オペレーティング システムのデフォルトのリンクハンドラで一般的なリンクを開く必要があります。SFSafariViewController ライブラリもサポートされているオプションです。

org_internal

リクエスト内の OAuth クライアント ID は、特定の Google Cloud 組織内の Google アカウントへのアクセスを制限するプロジェクトの一部です。この構成オプションの詳細については、OAuth 同意画面の設定に関するヘルプ記事のユーザータイプをご覧ください。

invalid_client

リクエストの送信元がこのクライアントに対して承認されていません。origin_mismatch をご覧ください。

invalid_grant

段階的な認可を使用している場合、トークンが期限切れになっているか、無効になっている可能性があります。 ユーザーを再度認証し、新しいトークンの取得についてユーザーの同意を得ます。このエラーが引き続き表示される場合は、アプリケーションが正しく構成されていることと、リクエストで正しいトークンとパラメータを使用していることを確認してください。それ以外の場合は、ユーザー アカウントが削除または無効化されている可能性があります。

origin_mismatch

認証リクエスト元の JavaScript のスキーム、ドメイン、ポートは、OAuth クライアント ID に登録されている承認済みの JavaScript 生成元 URI と一致していない場合があります。承認済みの JavaScript 生成元は Google API Console Credentials pageで確認できます。

redirect_uri_mismatch

承認リクエストで渡された redirect_uri が、OAuth クライアント ID の承認済みのリダイレクト URI と一致しません。 Google API Console Credentials pageで承認済みのリダイレクト URI を確認してください。

認証リクエスト元の JavaScript のスキーム、ドメイン、ポートは、OAuth クライアント ID に登録されている承認済みの JavaScript 生成元 URI と一致していない場合があります。 Google API Console Credentials pageで、承認済みの JavaScript 生成元を確認します。

redirect_uri パラメータは、サポートが終了し、サポートされなくなった OAuth 帯域外(OOB)フローを参照する場合があります。統合を更新するには、移行ガイドをご覧ください。

ステップ 4: OAuth 2.0 サーバー レスポンスを処理する

JS クライアント ライブラリ

JavaScript クライアント ライブラリは、Google の承認サーバーからのレスポンスを処理します。現在のユーザーのログイン状態の変化をモニタリングするようにリスナーを設定すると、ユーザーがアプリケーションに対して必要なアクセス権を付与したときにその関数が呼び出されます。

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 をクリックすると、このフローをテストできます。サンプルでは、Google ドライブ内のファイルのメタデータを表示するための読み取り専用アクセス権がリクエストされます。

https://accounts.google.com/o/oauth2/v2/auth?
 scope=https%3A//www.googleapis.com/auth/drive.metadata.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 で返される情報について詳しく説明します。

Google API の呼び出し

JS クライアント ライブラリ

アプリケーションがアクセス トークンを取得したら、JavaScript クライアント ライブラリを使用してユーザーに代わって API リクエストを行うことができます。クライアント ライブラリによってアクセス トークンが管理されるため、リクエストで送信するために特別な操作を行う必要はありません。

クライアント ライブラリは、API メソッドを呼び出す 2 つの方法をサポートしています。ディスカバリ ドキュメントを読み込むと、API によってメソッド固有の関数が定義されます。gapi.client.request 関数を使用して API メソッドを呼び出すこともできます。次の 2 つのスニペットは、Drive API の about.get メソッドについて、これらのオプションを示しています。

// Example 1: Use method-specific function
var request = gapi.client.drive.about.get({'fields': 'user'});

// Execute the API request.
request.execute(function(response) {
  console.log(response);
});


// Example 2: Use gapi.client.request(args) function
var request = gapi.client.request({
  'method': 'GET',
  'path': '/drive/v3/about',
  'params': {'fields': 'user'}
});
// Execute the API request.
request.execute(function(response) {
  console.log(response);
});

OAuth 2.0 エンドポイント

アプリケーションがアクセス トークンを取得した後、その API で必要なアクセス スコープが付与されている場合、特定のユーザー アカウントに代わって、このトークンを使用して Google API を呼び出すことができます。これを行うには、access_token クエリ パラメータまたは Authorization HTTP ヘッダー Bearer 値を指定して、API のリクエストにアクセス トークンを含めます。可能であれば、クエリ文字列はサーバーログに表示されるため、HTTP ヘッダーを使用することをおすすめします。ほとんどの場合、クライアント ライブラリを使用して Google API の呼び出しを設定できます(Drive Files API を呼び出すときなど)。

すべての Google API を試し、そのスコープは OAuth 2.0 Playground で確認できます。

HTTP GET の例

Authorization: Bearer HTTP ヘッダーを使用した drive.files エンドポイント(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);

サンプルコードの全文

JS クライアント ライブラリ

サンプルコードのデモ

このセクションでは、実際のアプリにおけるコードの動作を示すコードサンプルのデモを行います。アプリを承認すると、Google アカウントに接続されているアプリの一覧に表示されます。アプリの名称は「OAuth 2.0 Demo for Google API Docs」です。同様に、そのページへのアクセスを取り消してページを更新すると、そのアプリは表示されなくなります。

このアプリは https://www.googleapis.com/auth/drive.metadata.readonly スコープへのアクセスをリクエストします。このアクセス権は、JavaScript アプリケーションで OAuth 2.0 のフローを開始する方法を示すためにのみリクエストされます。このアプリは API リクエストを行いません。

JavaScript サンプルコード

上記のコードサンプルは、JavaScript 用の Google API クライアント ライブラリを読み込み、OAuth 2.0 フローを開始するページ(アプリ)用です。このページには、次のいずれかが表示されます。

  • ユーザーがアプリにログインするための 1 つのボタン。ユーザーがまだアプリを認証していない場合は、OAuth 2.0 フローを起動します。
  • ユーザーがアプリからログアウトすること、またはアプリに以前付与されたアクセス権を取り消すことが可能な 2 つのボタン。アプリからログアウトした場合、アプリに付与されたアクセス権は取り消されません。アプリがお客様に代わって他の承認済みリクエストを送信する前に、再度ログインする必要があります。ただし、次回アプリにアクセスする際にアクセス権を再度付与する場合は、アクセス権を再度付与する必要があります。

Google アカウントの [権限] ページでアプリへのアクセスを取り消すこともできます。アプリが Google API Docs の OAuth 2.0 デモとしてリストされている。

<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>

OAuth 2.0 エンドポイント

このコードサンプルは、JavaScript 用の Google API クライアント ライブラリを使用せずに JavaScript で OAuth 2.0 フローを完了する方法を示しています。このコードは、API リクエストを試すボタンを表示する HTML ページ用です。このボタンをクリックすると、コードはブラウザのローカル ストレージに API アクセス トークンが格納されているかどうかをチェックします。その場合、API リクエストが実行されます。それ以外の場合は、OAuth 2.0 フローを開始します。

OAuth 2.0 フローの場合、ページは次の手順を行います。

  1. これにより、ユーザーは Google の OAuth 2.0 サーバーに移動し、https://www.googleapis.com/auth/drive.metadata.readonly スコープへのアクセスがリクエストされます。
  2. リクエストされた 1 つ以上のスコープへのアクセス権を付与(または拒否)すると、ユーザーは元のページにリダイレクトされ、フラグメント識別子文字列からアクセス トークンが解析されます。
  3. このページでは、アクセス トークンを使用してサンプル API リクエストを行います。

    API リクエストは、Drive API の about.get メソッドを呼び出して、承認されたユーザーの Google ドライブ アカウントに関する情報を取得します。

  4. リクエストが正常に実行されると、ブラウザのデバッグ コンソールに API レスポンスが記録されます。

アプリのアクセス権は、Google アカウントの [権限] ページで取り消すことができます。アプリは Google API Docs の OAuth 2.0 デモとして表示されます。

このコードをローカルで実行するには、認証情報に対応する YOUR_CLIENT_ID 変数と YOUR_REDIRECT_URI 変数の値を設定する必要があります。YOUR_REDIRECT_URI 変数には、ページが提供される URL と同じ値を設定する必要があります。値は、 API Console Credentials pageで構成した 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';
  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>

JavaScript 生成元検証ルール

Google では、デベロッパーがアプリケーションを安全に保護できるよう、JavaScript 生成元に次の検証ルールを適用します。JavaScript 生成元は、これらのルールに従う必要があります。 以下で説明するドメイン、ホスト、スキームの定義については、RFC 3986 のセクション 3 をご覧ください。

検証ルール
スキーム

JavaScript 生成元は、プレーン HTTP ではなく HTTPS スキームを使用する必要があります。ローカルホスト URI(localhost IP アドレス URI を含む)は、このルールから除外されます。

ホスト

ホストに未加工の IP アドレスを指定することはできません。ローカルホストの IP アドレスはこのルールから除外されます。

ドメイン
  • ホスト TLD(トップレベル ドメイン)は、パブリック サフィックス リストに属している必要があります。
  • ホストドメインを “googleusercontent.com” にすることはできません。
  • JavaScript 生成元に URL 短縮ドメイン(goo.gl など)を含めることはできません。ただし、アプリがドメインを所有している場合を除きます。
  • ユーザー情報

    JavaScript 生成元に userinfo サブコンポーネントを含めることはできません。

    [Path]

    JavaScript 生成元にパス コンポーネントを含めることはできません。

    クエリ

    JavaScript 生成元にクエリ コンポーネントを含めることはできません。

    Fragment

    JavaScript 生成元にフラグメント コンポーネントを含めることはできません。

    キャラクター JavaScript 生成元に次のような文字を含めることはできません。
    • ワイルドカード文字('*'
    • 印刷できない ASCII 文字
    • パーセント エンコードが無効です(URL エンコード形式のパーセント記号の後に 16 進数 2 桁の数字が続く場合、パーセント エンコードは無効です)
    • null 文字(エンコードされた NULL 文字、%00%C0%80

    段階的な認可

    OAuth 2.0 プロトコルでは、アプリは、スコープによって識別されるリソースにアクセスするための承認をリクエストします。リソースに対して必要なタイミングで承認をリクエストすることは、ユーザー エクスペリエンスのベスト プラクティスとみなされています。それを可能にするため、Google の承認サーバーは段階的な認可をサポートしています。この機能により、必要に応じてスコープをリクエストできます。ユーザーが新しいスコープの権限を付与すると、ユーザーがプロジェクトに付与したすべてのスコープを含むトークンと交換される認証コードが返されます。

    たとえば、ユーザーが音楽トラックをサンプリングしてミックスを作成できるアプリでは、ログイン時に必要なリソースがほとんど必要としないため、ログインしたユーザーの名前しか必要としない可能性があります。ただし、完了したミックスを保存するには、Google ドライブへのアクセス権が必要になります。ほとんどのユーザーは、アプリが実際に必要としたときに Google ドライブへのアクセス許可を求められただけで自然になってしまうでしょう。

    この場合、ログイン時に、アプリは openid スコープと profile スコープをリクエストして基本ログインを実行し、その後、最初のリクエスト時に https://www.googleapis.com/auth/drive.file スコープをリクエストしてミックスを保存することができます。

    段階的な認可から取得したアクセス トークンには、次のルールが適用されます。

    • このトークンを使用して、新しい結合された認可にロール スコープされるスコープに対応するリソースにアクセスできます。
    • 統合承認の更新トークンを使用してアクセス トークンを取得する場合、アクセス トークンは統合承認を表し、レスポンスに含まれる任意の scope 値に使用できます。
    • 複数のクライアントから権限付与が要求された場合でも、API プロジェクトにユーザーが付与したすべてのスコープが組み合わされます。たとえば、ユーザーがアプリケーションのデスクトップ クライアントを使用して 1 つのスコープへのアクセス権を付与し、その後、モバイル クライアントを介して同じアプリに別のスコープを付与した場合、統合された認可には両方のスコープが含まれます。
    • 統合された承認を表すトークンを取り消すと、関連するユーザーに代わって、その承認のスコープすべてへのアクセス権が同時に取り消されます。

    次のコードサンプルは、既存のアクセス トークンにスコープを追加する方法を示しています。このアプローチにより、アプリで複数のアクセス トークンを管理する必要がなくなります。

    JS クライアント ライブラリ

    既存のアクセス トークンにスコープを追加するには、GoogleUser.grant(options) メソッドを呼び出します。options オブジェクトは、アクセス権を付与する追加のスコープを識別します。

    // Space-separated list of additional scope(s) you are requesting access to.
    // This code adds read-only access to the user's calendars via the Calendar API.
    var NEW_SCOPES = 'https://www.googleapis.com/auth/calendar.readonly';
    
    // Retrieve the GoogleUser object for the current user.
    var GoogleUser = GoogleAuth.currentUser.get();
    GoogleUser.grant({'scope': NEW_SCOPES});

    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 リクエストを追加することが可能です。

    JS クライアント ライブラリ

    プログラムでトークンを取り消すには、 GoogleAuth.disconnect() を呼び出します。

    GoogleAuth.disconnect();

    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();
    }