JavaScript ウェブ アプリケーションに OAuth 2.0 を使用する

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

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

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

Google API クライアント ライブラリと Google Identity Services

JavaScript 用 Google API クライアント ライブラリを使用して Google に対して承認された呼び出しを行う場合は、 Google Identity Services JavaScript ライブラリを使用して OAuth 2.0 フローを処理する必要があります。OAuth 2.0 の暗黙的権限付与フローに基づく Google ID サービスのトークンモデルをご覧ください。

前提条件

プロジェクトで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. [ライブラリ] ページで YouTube Data API を見つけて有効にします。アプリケーションで使用する他の API を見つけて、それらも有効にしてください。

承認認証情報を作成する

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 認証の実装を開始する前に、アプリがアクセス権限を必要とするスコープを特定することをおすすめします。

YouTube Data API v3 では、次のスコープを使用します。

スコープ
https://www.googleapis.com/auth/youtubeYouTube アカウントの管理
https://www.googleapis.com/auth/youtube.channel-memberships.creator現在アクティブなチャンネル メンバー、メンバーの現在のレベル、いつメンバーになったかをリストで確認する
https://www.googleapis.com/auth/youtube.force-sslYouTube 動画、評価、コメント、字幕の表示、編集、完全削除
https://www.googleapis.com/auth/youtube.readonlyYouTube アカウントの表示
https://www.googleapis.com/auth/youtube.uploadYouTube 動画の管理
https://www.googleapis.com/auth/youtubepartnerYouTube のアセットや関連するコンテンツの表示と管理
https://www.googleapis.com/auth/youtubepartner-channel-auditYouTube パートナーの監査プロセス時に関連する YouTube チャンネルの個人情報の表示

Google API へのアクセスに使用できるスコープの一覧については、OAuth 2.0 API スコープのドキュメントをご覧ください。

OAuth 2.0 アクセス トークンの取得

次の手順は、アプリケーションが Google の OAuth 2.0 サーバーと連携し、ユーザーの代わりに API リクエストを実行することについてユーザーの同意を取得する方法を示しています。ユーザーの承認が必要な Google API リクエストをアプリケーションで実行するには、アプリケーションでその同意を得る必要があります。

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

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

OAuth 2.0 エンドポイント

https://accounts.google.com/o/oauth2/v2/auth にある URL を生成し、Google の OAuth 2.0 エンドポイントからのアクセスをリクエストします。このエンドポイントには 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 がユーザーに表示する同意画面を通知します。

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

YouTube Data API v3 では、次のスコープを使用します。

スコープ
https://www.googleapis.com/auth/youtubeYouTube アカウントの管理
https://www.googleapis.com/auth/youtube.channel-memberships.creator現在アクティブなチャンネル メンバー、メンバーの現在のレベル、いつメンバーになったかをリストで確認する
https://www.googleapis.com/auth/youtube.force-sslYouTube 動画、評価、コメント、字幕の表示、編集、完全削除
https://www.googleapis.com/auth/youtube.readonlyYouTube アカウントの表示
https://www.googleapis.com/auth/youtube.uploadYouTube 動画の管理
https://www.googleapis.com/auth/youtubepartnerYouTube のアセットや関連するコンテンツの表示と管理
https://www.googleapis.com/auth/youtubepartner-channel-auditYouTube パートナーの監査プロセス時に関連する YouTube チャンネルの個人情報の表示

Google API へのアクセスに使用できるスコープの一覧については、OAuth 2.0 API スコープのドキュメントをご覧ください。

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

state 推奨

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

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

include_granted_scopes 省略可

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

login_hint 省略可

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

パラメータ値をメールアドレスまたは sub 識別子に設定します。これは、ユーザーの Google ID に相当します。

prompt 省略可

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

指定できる値は次のとおりです。

none 認証画面や同意画面は表示しないでください。他の値と一緒に指定することはできません。
consent ユーザーに同意を求めます。
select_account アカウントの選択をユーザーに促します。

Google の承認サーバーへのリダイレクトの例

以下のサンプル URL は、ユーザーの YouTube アカウントを表示するアクセスを許可するスコープへのオフライン アクセス(access_type=offline)をリクエストします。増分認可を使用して、ユーザーが以前にアプリケーションへのアクセスを許可したスコープを新しいアクセス トークンでカバーするようにします。この URL では、必須の redirect_uri パラメータ、response_type パラメータ、client_id パラメータと state パラメータの値も設定されます。読みやすくするために、URL には改行とスペースが含まれています。

https://accounts.google.com/o/oauth2/v2/auth?
 scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fyoutube.readonly&
 include_granted_scopes=true&
 state=state_parameter_passthrough_value&
 redirect_uri=http%3A%2F%2Flocalhost%2Foauth2callback&
 response_type=token&
 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/youtube.force-ssl',
                '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 管理者のポリシーにより、Google アカウントでリクエストされた 1 つ以上のスコープを承認できません。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 同意画面の設定」のユーザータイプのセクションをご覧ください。

invalid_client

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

invalid_grant

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

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

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 ドライブ内のファイルのメタデータを表示するための読み取り専用アクセスをリクエストします。

https://accounts.google.com/o/oauth2/v2/auth?
 scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fyoutube.readonly&
 include_granted_scopes=true&
 state=state_parameter_passthrough_value&
 redirect_uri=http%3A%2F%2Flocalhost%2Foauth2callback&
 response_type=token&
 client_id=client_id

OAuth 2.0 フローを完了すると、http://localhost/oauth2callback にリダイレクトされます。ローカルマシンがそのアドレスでファイルを提供しない限り、その URL は 404 NOT FOUND エラーになります。次のステップでは、ユーザーがアプリケーションにリダイレクトされたときに URI で返される情報の詳細を説明します。

Google API の呼び出し

OAuth 2.0 エンドポイント

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

YouTube Data API は、複数の YouTube チャンネル(レコード レーベルや映画スタジオなど)を所有、管理する YouTube コンテンツ所有者向けのサービス アカウントのみをサポートしています。

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

HTTP GET の例

Authorization: Bearer HTTP ヘッダーを使用した youtube.channels エンドポイント(YouTube Data API)の呼び出しは次のようになります。独自のアクセス トークンを指定する必要があります。

GET /youtube/v3/channels?part=snippet&mine=true HTTP/1.1
Host: www.googleapis.com
Authorization: Bearer access_token

access_token クエリ文字列パラメータを使用した、認証されたユーザーに対して同じ API の呼び出しの例を次に示します。

GET https://www.googleapis.com/youtube/v3/channels?access_token=access_token&part=snippet&mine=true

curl の例

これらのコマンドは、curl コマンドライン アプリケーションを使用してテストできます。HTTP ヘッダー オプションを使用した例を次に示します(推奨)。

curl -H "Authorization: Bearer access_token" https://www.googleapis.com/youtube/v3/channels?part=snippet&mine=true

または、クエリ文字列パラメータ オプションを使用します。

curl https://www.googleapis.com/youtube/v3/channels?access_token=access_token&part=snippet&mine=true

JavaScript サンプルコード

以下のコード スニペットは、CORS(クロスオリジン リソース シェアリング)を使用して Google API にリクエストを送信する方法を示しています。この例では、JavaScript 用の Google API クライアント ライブラリを使用しません。ただし、クライアント ライブラリを使用していない場合でも、これらのリクエストについて理解を深めるには、クライアント ライブラリのドキュメントにある CORS サポートガイドをご覧ください。

このコード スニペットの access_token 変数は、承認済みユーザーの代わりに API リクエストを行うために取得したトークンを表します。完全な例は、このトークンをブラウザのローカル ストレージに格納し、API リクエストの際にトークンを取得する方法を示しています。

var xhr = new XMLHttpRequest();
xhr.open('GET',
    'https://www.googleapis.com/youtube/v3/channels?part=snippet&mine=true&' +
    '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 フローの場合、このページは次の手順を行います。

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

    この API リクエストは、YouTube Data API の channels.list メソッドを呼び出して、承認されたユーザーの YouTube チャンネルに関するデータを取得します。

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

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

このコードをローカルで実行するには、認可の認証情報に対応する YOUR_CLIENT_ID 変数と YOUR_REDIRECT_URI 変数の値を設定する必要があります。YOUR_REDIRECT_URI 変数には、ページを配信する URL と同じ 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';

  // 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']) {
      var xhr = new XMLHttpRequest();
      xhr.open('GET',
          'https://www.googleapis.com/youtube/v3/channels?part=snippet&mine=true&' +
          '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() {
    // 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/youtube.force-ssl',
                  '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(localhost IP アドレス URI を含む)は、このルールから除外されます。

ホスト

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

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

    JavaScript のオリジンに userinfo サブコンポーネントを含めることはできません。

    [パス]

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

    Query

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

    Fragment

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

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

    段階的な認可

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

    たとえば、ユーザーが興味深いローカル イベントを特定するのに役立つアプリがあるとします。このアプリでは、イベントに関する動画を視聴したり、動画を評価したり、再生リストに動画を追加したりできます。また、アプリを使用して Google カレンダーに予定を追加することもできます。

    この場合、ログイン時に、アプリはスコープへのアクセスを必要としない、またはリクエストしない可能性があります。ただし、ユーザーが動画の評価、再生リストへの動画の追加、または別の YouTube アクションを実行しようとした場合、アプリは https://www.googleapis.com/auth/youtube.force-ssl スコープへのアクセスをリクエストする可能性があります。同様に、ユーザーがカレンダーの予定を追加しようとした場合、アプリは https://www.googleapis.com/auth/calendar スコープへのアクセス権をリクエストできます。

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

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

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

    OAuth 2.0 エンドポイント

    この例では、呼び出し元のアプリケーションから、ユーザーの YouTube アナリティクス データを取得するためのアクセス権に加えて、ユーザーがすでにアプリケーションに付与しているアクセス権もリクエストします。

    既存のアクセス トークンにスコープを追加するには、Google の OAuth 2.0 サーバーへのリクエストinclude_granted_scopes パラメータを含めます。

    次のコード スニペットは、その方法を示しています。このスニペットは、アクセス トークンが有効なスコープがブラウザのローカル ストレージに保存されていることを前提としています。(完全なサンプルコードでは、ブラウザのローカル ストレージに oauth2-test-params.scope プロパティを設定することで、アクセス トークンが有効なスコープのリストが格納されています)。

    スニペットでは、アクセス トークンが有効なスコープと、特定のクエリに使用するスコープが比較されます。アクセス トークンがそのスコープをカバーしていない場合、OAuth 2.0 フローが開始されます。ここで、oauth2SignIn 関数はステップ 2 で提供された関数と同じです(これは完全な例の後半で説明します)。

    var SCOPE = 'https://www.googleapis.com/auth/youtube.force-ssl';
    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();
    }