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

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

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

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

Prerequisites

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

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

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

  1. Open the API Library は Google API Consoleにあります。
  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 アクセス トークンの取得

次の手順は、アプリケーションが Google の OAuth 2.0 サーバーとやり取りし、ユーザーに代わって API リクエストを実行することについてユーザーの同意を得る方法を示しています。アプリケーションは、ユーザー承認が必要な 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 ディスカバリ ドキュメントのリストを指定します。ディスカバリ ドキュメントには、リソース スキーマを含む API のサーフェスが記述されます。JavaScript クライアント ライブラリは、この情報を使用して、アプリケーションで使用できるメソッドを生成します。このサンプルコードは、Google Drive API のバージョン 3 のディスカバリ ドキュメントを取得します。

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

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 エンドポイント

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

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

パラメータ
client_id 必須

アプリケーションのクライアント ID。この値は、 API ConsoleCredentials pageにあります。

redirect_uri 必須

ユーザーが承認フローを完了した後、API サーバーがユーザーをリダイレクトする場所を決定します。この値は、クライアントで構成した OAuth 2.0 クライアントの承認済みリダイレクト URI の API Console Credentials pageと正確に一致している必要があります。この値が、指定された 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 認証サーバーにヒントを提供できます。サーバーはヒントを使用して、ログインフォームにメールアドレスのフィールドを事前に入力するか、適切なマルチログイン セッションを選択することにより、ログインフローを簡素化します。

パラメータ値には、ユーザーの Google ID に相当するメールアドレスまたは sub 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 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 の Android 用 AppAuth などの Android ライブラリを使用する必要があります。

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

iOS

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

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

org_internal

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

origin_mismatch

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

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 生成元を確認します。

ステップ 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. ユーザーを https://www.googleapis.com/auth/drive.metadata.readonly スコープへのアクセスを要求する Google の OAuth 2.0 サーバーに転送します。
  2. リクエストされた 1 つ以上のスコープへのアクセスを許可(または拒否)した後、ユーザーは元のページにリダイレクトされ、フラグメント識別子文字列のアクセス トークンを解析します。
  3. このページでは、アクセス トークンを使用してサンプルの API リクエストを行います。

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

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

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

このコードをローカルで実行するには、認証情報に対応する 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)を含めることはできません。
  • Userinfo

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

    [Path]

    JavaScript 生成元にはパス要素を含めることはできません。

    検索キーワード

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

    フラグメント

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

    文字 JavaScript 生成元には、次のような特定の文字を含めることはできません。
    • ワイルドカード文字('*'
    • 印刷できない ASCII 文字
    • パーセント エンコードが無効です(URL エンコード形式のパーセント記号に 2 桁の 16 進数記号を追加しない任意のパーセント エンコード)
    • Null 文字(エンコードされた NULL 文字、例:%00%C0%80

    増分承認

    OAuth 2.0 プロトコルでは、アプリはスコープによって識別されるリソースにアクセスするための承認をリクエストします。リソースの承認を必要なときにリクエストすることをおすすめします。これを可能にするため、Google の承認サーバーは増分承認に対応しています。この機能を使用すると、必要に応じてスコープをリクエストできます。ユーザーが新しいスコープの権限を付与すると、ユーザーがプロジェクトに付与したすべてのスコープを含むトークンと交換できる認証コードを返します。

    たとえば、ユーザーが音楽トラックをサンプリングしてミックスを作成できるアプリでは、ログイン時に必要となるリソースがほとんどなく、ログインする人の名前だけで十分です。ただし、完成したミックスを保存するには、Google ドライブにアクセスする必要があります。ほとんどのユーザーは、実際にアプリが必要とした時点で Google ドライブへのアクセスを要求するだけで、自然にできませんでした。

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

    増分承認から取得したアクセス トークンには、以下のルールが適用されます。

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

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

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