このドキュメントでは、ウェブサーバー アプリケーションが Google API クライアント ライブラリまたは Google OAuth 2.0 エンドポイントを使用して、YouTube Data API にアクセスするための OAuth 2.0 認証を実装する方法について説明します。
OAuth 2.0 を使用すると、ユーザー名やパスワードなどの情報を非公開にしたまま、特定のデータをアプリケーションと共有することができます。たとえば、OAuth 2.0 を使用して、チャンネルの YouTube データを取得する権限を取得できます。
この OAuth 2.0 フローはユーザー認証専用です。機密情報を保存し、状態を維持できるアプリケーション向けに設計されています。適切に承認されたウェブサーバー アプリケーションは、ユーザーがアプリケーションを操作する間、またはユーザーがアプリケーションを離れた後に API にアクセスできます。
ウェブサーバー アプリケーションでは、API リクエストの承認にも サービス アカウントが頻繁に使用されます。特に、Cloud APIs を呼び出してユーザー固有のデータではなくプロジェクト ベースのデータにアクセスする場合に、サービス アカウントが使用されます。ウェブサーバー アプリケーションでは、サービス アカウントをユーザー承認と組み合わせて使用できます。
YouTube Live Streaming API は、サービス アカウントのフローをサポートしていません。サービス アカウントを YouTube アカウントにリンクする方法はないため、このフローでリクエストを承認しようとすると、NoLinkedYouTubeAccount
エラーが発生します。
クライアント ライブラリ
このページの言語固有の例では、Google API クライアント ライブラリを使用して OAuth 2.0 認証を実装しています。コードサンプルを実行するには、まずご使用の言語のクライアント ライブラリをインストールする必要があります。
Google API クライアント ライブラリを使用してアプリケーションの OAuth 2.0 フローを処理する場合、クライアント ライブラリは多くのアクションを実行しますが、これらのアクションはアプリケーションが独自に処理する必要があります。たとえば、保存されているアクセス トークンをアプリケーションが使用または更新できるタイミングや、アプリケーションが同意を再取得する必要があるタイミングを決定します。また、クライアント ライブラリは正しいリダイレクト URL を生成し、認証コードをアクセス トークンと交換するリダイレクト ハンドラの実装に役立ちます。
サーバーサイド アプリケーション用の Google API クライアント ライブラリは、次の言語で利用できます。
前提条件
プロジェクトでAPI を有効にする
Google API を呼び出すアプリケーションは、 API Consoleでこれらの API を有効にする必要があります。
プロジェクトで API を有効にするには:
- Google API Console内のOpen the API Library 。
- If prompted, select a project, or create a new one.
- [ライブラリ] ページでは、YouTube Data API を探して有効にします。アプリケーションが使用する他の API を見つけて、それらも有効にします。
承認認証情報を作成する
OAuth 2.0 を使用して Google API にアクセスするアプリケーションには、Google の OAuth 2.0 サーバーに対してそのアプリケーションを識別する認証情報が必要です。次の手順では、プロジェクトの認証情報を作成する方法について説明します。アプリケーションはこの認証情報を使用して、そのプロジェクトで有効にした API にアクセスできます。
- Go to the Credentials page.
- [認証情報を作成] > [OAuth クライアント ID] をクリックします。
- アプリケーションの種類として [ウェブ アプリケーション] を選択します。
- フォームに入力し、[作成] をクリックします。PHP、Java、Python、Ruby、.NET などの言語やフレームワークを使用するアプリケーションでは、承認済みのリダイレクト URI を指定する必要があります。リダイレクト URI は、OAuth 2.0 サーバーがレスポンスを送信できるエンドポイントです。これらのエンドポイントは、Google の検証ルールに準拠している必要があります。
テスト用に、ローカルマシンを参照する URI(
http://localhost:8080
など)を指定できます。このドキュメントのすべての例で、リダイレクト URI としてhttp://localhost:8080
を使用していることに注意してください。アプリケーションが認証コードをページ上の他のリソースに公開しないように、アプリの認証エンドポイントを設計することをおすすめします。
認証情報を作成したら、 API Consoleから client_secret.json ファイルをダウンロードします。自分のアプリケーションだけがアクセスできる場所にファイルを安全に保存します。
アクセス スコープを特定する
スコープを使用すると、アプリケーションが必要なリソースへのアクセス権のみをリクエストできます。また、ユーザーはアプリケーションに付与するアクセス権の量を制御できます。したがって、リクエストするスコープの数とユーザーの同意を得る可能性の間に逆相関関係がある場合があります。
OAuth 2.0 認証の実装を開始する前に、アプリがアクセス権限を必要とするスコープを特定することをおすすめします。
また、アプリケーションは、段階的承認プロセスを使用して認可スコープへのアクセスをリクエストすることを推奨します。このプロセスでは、アプリケーションは状況に応じてユーザーデータへのアクセスをリクエストします。このベスト プラクティスは、アプリがアクセスをリクエストしている理由をユーザーがわかりやすくするのに役立ちます。
YouTube Data API v3 では、次のスコープを使用します。
スコープ | |
---|---|
https://www.googleapis.com/auth/youtube | YouTube アカウントの管理 |
https://www.googleapis.com/auth/youtube.channel-memberships.creator | 現在アクティブなチャンネル メンバー、メンバーの現在のレベル、いつメンバーになったかをリストで確認する |
https://www.googleapis.com/auth/youtube.force-ssl | YouTube 動画、評価、コメント、字幕の表示、編集、完全削除 |
https://www.googleapis.com/auth/youtube.readonly | YouTube アカウントの表示 |
https://www.googleapis.com/auth/youtube.upload | YouTube 動画の管理 |
https://www.googleapis.com/auth/youtubepartner | YouTube のアセットや関連するコンテンツの表示と管理 |
https://www.googleapis.com/auth/youtubepartner-channel-audit | YouTube パートナーの監査プロセス時に関連する YouTube チャンネルの個人情報の表示 |
OAuth 2.0 API スコープのドキュメントには、Google API へのアクセスに使用できるスコープの一覧が記載されています。
言語固有の要件
このドキュメントのコードサンプルを実行するには、Google アカウント、インターネットへのアクセス、ウェブブラウザが必要です。API クライアント ライブラリを使用している場合は、以下の言語固有の要件もご覧ください。
PHP
このドキュメントの PHP コードサンプルを実行するには、次のものが必要です。
- コマンドライン インターフェース(CLI)と JSON 拡張機能がインストールされている PHP 5.6 以降。
- Composer 依存関係管理ツール。
-
PHP の Google API クライアント ライブラリ:
composer require google/apiclient:^2.10
Python
このドキュメントの Python コードサンプルを実行するには、次のものが必要です。
- Python 2.6 以降
- pip パッケージ管理ツール。
- Python 用の Google API クライアント ライブラリ:
pip install --upgrade google-api-python-client
- ユーザー承認のための
google-auth
、google-auth-oauthlib
、google-auth-httplib2
。pip install --upgrade google-auth google-auth-oauthlib google-auth-httplib2
- Flask Python ウェブ アプリケーション フレームワーク。
pip install --upgrade flask
requests
HTTP ライブラリ。pip install --upgrade requests
Ruby
このドキュメントの Ruby コードサンプルを実行するには、次のものが必要です。
- Ruby 2.6 以降
-
Ruby 用の Google 認証ライブラリ:
gem install googleauth
-
Sinatra Ruby ウェブ アプリケーション フレームワーク。
gem install sinatra
Node.js
このドキュメントの Node.js コードサンプルを実行するには、次のものが必要です。
- Node.js のメンテナンス LTS、アクティブな LTS、現在のリリース。
-
Google API Node.js クライアント:
npm install googleapis crypto express express-session
HTTP/REST
ライブラリをインストールしなくても、OAuth 2.0 エンドポイントを直接呼び出すことができます。
OAuth 2.0 アクセス トークンの取得
以下の手順は、アプリケーションが Google の OAuth 2.0 サーバーとやり取りし、ユーザーの代わりに API リクエストを実行することについてユーザーの同意を取得する方法を示しています。アプリケーションでユーザー承認を必要とする Google API リクエストを実行するには、この同意を得る必要があります。
以下のリストは、これらのステップの概要をまとめたものです。
- アプリが必要な権限を特定します。
- アプリケーションは、リクエストされた権限のリストとともにユーザーを Google にリダイレクトします。
- ユーザーは、アプリケーションに権限を付与するかどうかを決定します。
- アプリケーションは、ユーザーが何を決定したかを調べます。
- ユーザーがリクエストされた権限を付与した場合、アプリケーションはユーザーの代わりに API リクエストを行うために必要なトークンを取得します。
ステップ 1: 認可パラメータを設定する
まず、承認リクエストを作成します。このリクエストでは、アプリを識別するパラメータを設定し、ユーザーがアプリに付与するようユーザーに要求する権限を定義します。
- OAuth 2.0 の認証と認可に Google クライアント ライブラリを使用する場合は、これらのパラメータを定義するオブジェクトを作成して構成します。
- Google OAuth 2.0 エンドポイントを直接呼び出す場合は、URL を生成し、その URL にパラメータを設定します。
以下のタブでは、ウェブサーバー アプリケーションでサポートされている認証パラメータを定義します。言語固有の例は、クライアント ライブラリまたは認可ライブラリを使用して、これらのパラメータを設定するオブジェクトを構成する方法も示しています。
PHP
以下のコード スニペットでは、承認リクエストのパラメータを定義する Google\Client()
オブジェクトを作成します。
このオブジェクトは、client_secret.json ファイル内の情報を使用してアプリケーションを識別します。(このファイルの詳細については、認証情報の作成をご覧ください)。また、このオブジェクトは、アプリケーションがアクセス権限をリクエストしているスコープと、Google の OAuth 2.0 サーバーからのレスポンスを処理するアプリケーションの認証エンドポイントへの URL を識別します。最後に、省略可能な access_type
パラメータと include_granted_scopes
パラメータを設定します。
たとえば、ユーザーの YouTube データを取得するためのオフライン アクセスをリクエストするには、次のようにします。
$client = new Google\Client(); // Required, call the setAuthConfig function to load authorization credentials from // client_secret.json file. $client->setAuthConfig('client_secret.json'); // Required, to set the scope value, call the addScope function $client->addScope(Google_Service_YouTube::YOUTUBE_FORCE_SSL); // Required, call the setRedirectUri function to specify a valid redirect URI for the // provided client_id $client->setRedirectUri('http://' . $_SERVER['HTTP_HOST'] . '/oauth2callback.php'); // Recommended, offline access will give you both an access and refresh token so that // your app can refresh the access token without user interaction. $client->setAccessType('offline'); // Recommended, call the setState function. Using a state value can increase your assurance that // an incoming connection is the result of an authentication request. $client->setState($sample_passthrough_value); // Optional, if your application knows which user is trying to authenticate, it can use this // parameter to provide a hint to the Google Authentication Server. $client->setLoginHint('hint@example.com'); // Optional, call the setPrompt function to set "consent" will prompt the user for consent $client->setPrompt('consent'); // Optional, call the setIncludeGrantedScopes function with true to enable incremental // authorization $client->setIncludeGrantedScopes(true);
Python
次のコード スニペットでは、google-auth-oauthlib.flow
モジュールを使用して承認リクエストを作成しています。
このコードは Flow
オブジェクトを作成します。このオブジェクトは、認証情報の作成後にダウンロードした client_secret.json ファイル内の情報を使用してアプリケーションを識別します。また、このオブジェクトは、アプリケーションがアクセス権限をリクエストしているスコープと、Google の OAuth 2.0 サーバーからのレスポンスを処理するアプリケーションの認証エンドポイントへの URL を識別します。最後に、このコードはオプションの access_type
パラメータと include_granted_scopes
パラメータを設定します。
たとえば、ユーザーの YouTube データを取得するためのオフライン アクセスをリクエストするには、次のようにします。
import google.oauth2.credentials import google_auth_oauthlib.flow # Required, call the from_client_secrets_file method to retrieve the client ID from a # client_secret.json file. The client ID (from that file) and access scopes are required. (You can # also use the from_client_config method, which passes the client configuration as it originally # appeared in a client secrets file but doesn't access the file itself.) flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file( 'client_secret.json', scopes=['https://www.googleapis.com/auth/youtube.force-ssl']) # Required, indicate where the API server will redirect the user after the user completes # the authorization flow. The redirect URI is required. The value must exactly # match one of the authorized redirect URIs for the OAuth 2.0 client, which you # configured in the API Console. If this value doesn't match an authorized URI, # you will get a 'redirect_uri_mismatch' error. flow.redirect_uri = 'https://www.example.com/oauth2callback' # Generate URL for request to Google's OAuth 2.0 server. # Use kwargs to set optional request parameters. authorization_url, state = flow.authorization_url( # Recommended, enable offline access so that you can refresh an access token without # re-prompting the user for permission. Recommended for web server apps. access_type='offline', # Optional, enable incremental authorization. Recommended as a best practice. include_granted_scopes='true', # Optional, if your application knows which user is trying to authenticate, it can use this # parameter to provide a hint to the Google Authentication Server. login_hint='hint@example.com', # Optional, set prompt to 'consent' will prompt the user for consent prompt='consent')
Ruby
作成した client_secrets.json ファイルを使用して、アプリケーションのクライアント オブジェクトを構成します。クライアント オブジェクトを構成するときは、アプリケーションがアクセスする必要があるスコープと、OAuth 2.0 サーバーからのレスポンスを処理するアプリケーションの認証エンドポイントへの URL を指定します。
たとえば、ユーザーの YouTube データを取得するためのオフライン アクセスをリクエストするには、次のようにします。
require 'google/apis/youtube_v3' require "googleauth" require 'googleauth/stores/redis_token_store' client_id = Google::Auth::ClientId.from_file('/path/to/client_secret.json') scope = 'https://www.googleapis.com/auth/youtube.force-ssl' token_store = Google::Auth::Stores::RedisTokenStore.new(redis: Redis.new) authorizer = Google::Auth::WebUserAuthorizer.new(client_id, scope, token_store, '/oauth2callback')
アプリケーションは、このクライアント オブジェクトを使用して、認可リクエスト URL の生成や HTTP リクエストへのアクセス トークンの適用など、OAuth 2.0 オペレーションを実行します。
Node.js
次のコード スニペットは、承認リクエストのパラメータを定義する google.auth.OAuth2
オブジェクトを作成します。
このオブジェクトは、client_secret.json ファイルの情報を使用してアプリケーションを識別します。アクセス トークンを取得する権限をユーザーにリクエストするには、ユーザーを同意ページにリダイレクトします。同意ページの URL を作成するには:
const {google} = require('googleapis'); const crypto = require('crypto'); const express = require('express'); const session = require('express-session'); /** * To use OAuth2 authentication, we need access to a CLIENT_ID, CLIENT_SECRET, AND REDIRECT_URI * from the client_secret.json file. To get these credentials for your application, visit * https://console.cloud.google.com/apis/credentials. */ const oauth2Client = new google.auth.OAuth2( YOUR_CLIENT_ID, YOUR_CLIENT_SECRET, YOUR_REDIRECT_URL ); // Access scopes for read-only Drive activity. const scopes = [ 'https://www.googleapis.com/auth/drive.metadata.readonly' ]; // Generate a secure random state value. const state = crypto.randomBytes(32).toString('hex'); // Store state in the session req.session.state = state; // Generate a url that asks permissions for the Drive activity scope const authorizationUrl = oauth2Client.generateAuthUrl({ // 'online' (default) or 'offline' (gets refresh_token) access_type: 'offline', /** Pass in the scopes array defined above. * Alternatively, if only one scope is needed, you can pass a scope URL as a string */ scope: scopes, // Enable incremental authorization. Recommended as a best practice. include_granted_scopes: true, // Include the state parameter to reduce the risk of CSRF attacks. state: state });
重要な注意事項 - refresh_token
は最初の承認でのみ返されます。詳しくは
こちらをご覧ください。
HTTP/REST
Google の OAuth 2.0 エンドポイントは https://accounts.google.com/o/oauth2/v2/auth
にあります。このエンドポイントには HTTPS を介してのみアクセスできます。プレーンの HTTP 接続は拒否されます。
Google 承認サーバーは、ウェブサーバー アプリケーション向けに次のクエリ文字列パラメータをサポートしています。
パラメータ | |||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
client_id |
必須 アプリケーションのクライアント ID。この値は API Console Credentials pageで確認できます。 |
||||||||||||||||
redirect_uri |
必須
ユーザーが認可フローを完了した後、API サーバーがユーザーをリダイレクトする場所を指定します。この値は、クライアントの API Console
Credentials pageで構成した OAuth 2.0 クライアントで承認済みのリダイレクト URI のいずれかと完全に一致する必要があります。この値が、指定された
|
||||||||||||||||
response_type |
必須
Google OAuth 2.0 エンドポイントが認証コードを返すかどうかを決定します。 ウェブサーバー アプリケーションのパラメータ値は |
||||||||||||||||
scope |
必須 ユーザーの代わりにアプリケーションがアクセスできるリソースを識別するスコープのスペース区切りリスト。これらの値により、Google がユーザーに表示する同意画面が決まります。 スコープを使用すると、アプリケーションが必要とするリソースへのアクセス権のみをリクエストできると同時に、ユーザーはアプリケーションに付与するアクセス権の量を制御できます。したがって、リクエストするスコープの数とユーザーの同意を得る可能性には逆相関があります。 YouTube Data API v3 では、次のスコープを使用します。
OAuth 2.0 API スコープのドキュメントには、Google API へのアクセスに使用できるスコープの一覧が記載されています。 可能な限り、コンテキストに応じて承認スコープへのアクセスをアプリケーションからリクエストすることをおすすめします。段階的承認により、状況に応じてユーザーデータへのアクセスをリクエストすると、アプリがそのアクセスを必要とする理由をユーザーが簡単に理解できるようになります。 |
||||||||||||||||
access_type |
推奨
ユーザーがブラウザにアクセスしていないときに、アプリケーションがアクセス トークンを更新できるかどうかを示します。有効なパラメータ値は、デフォルト値である ユーザーがブラウザにアクセスしていないときにアプリケーションでアクセス トークンを更新する必要がある場合は、値を |
||||||||||||||||
state |
推奨
アプリケーションが認可リクエストと認可サーバーのレスポンスの間で状態を維持するために使用する任意の文字列値を指定します。ユーザーがアプリケーションのアクセス リクエストを承諾または拒否すると、サーバーは、 このパラメータは、ユーザーをアプリ内の適切なリソースに誘導する、ノンスを送信する、クロスサイト リクエスト フォージェリを緩和するなど、さまざまな目的で使用できます。 |
||||||||||||||||
include_granted_scopes |
省略可
アプリケーションは増分認証を使用して、コンテキストに応じて追加のスコープへのアクセスをリクエストできます。このパラメータの値を |
||||||||||||||||
enable_granular_consent |
省略可
デフォルトは Google がアプリケーションに対してきめ細かい権限を有効にすると、このパラメータは無効になります。 |
||||||||||||||||
login_hint |
省略可
認証対象のユーザーをアプリケーションが認識している場合、このパラメータを使用して、Google 認証サーバーにヒントを提供できます。サーバーはこのヒントを使用して、ログイン フォームのメール フィールドに事前入力するか、適切なマルチログイン セッションを選択することで、ログインフローを簡素化します。 パラメータ値をメールアドレスまたは |
||||||||||||||||
prompt |
省略可
ユーザーに提示するプロンプトのスペース区切りリスト(大文字と小文字を区別)。このパラメータを指定しない場合は、プロジェクトが初めてアクセスをリクエストしたときにのみ、ユーザーにメッセージが表示されます。詳しくは、 再同意を求めるをご覧ください。 指定できる値は次のとおりです。
|
ステップ 2: Google の OAuth 2.0 サーバーにリダイレクトする
認証と認可のプロセスを開始するために、ユーザーを Google の OAuth 2.0 サーバーにリダイレクトします。これは通常、アプリケーションがユーザーのデータに最初にアクセスする必要があるときに発生します。増分承認の場合、このステップは、まだアクセス権限がない追加のリソースにアプリが最初にアクセスする必要があるときにも行われます。
PHP
- Google の OAuth 2.0 サーバーにアクセスをリクエストするための URL を生成します。
$auth_url = $client->createAuthUrl();
- ユーザーを
$auth_url
にリダイレクトします。header('Location: ' . filter_var($auth_url, FILTER_SANITIZE_URL));
Python
次の例は、Flask ウェブ アプリケーション フレームワークを使用してユーザーを認証 URL にリダイレクトする方法を示しています。
return flask.redirect(authorization_url)
Ruby
- Google の OAuth 2.0 サーバーにアクセスをリクエストするための URL を生成します。
auth_uri = authorizer.get_authorization_url(login_hint: user_id, request: request)
- ユーザーを
auth_uri
にリダイレクトします。
Node.js
-
ステップ 1 で生成された URL
authorizationUrl
のgenerateAuthUrl
メソッドを使用して、Google の OAuth 2.0 サーバーへのアクセスをリクエストします。 -
ユーザーを
authorizationUrl
にリダイレクトします。res.redirect(authorizationUrl);
HTTP/REST
Sample redirect to Google's authorization server
An example URL is shown below, with line breaks and spaces for readability. The URL requests
access to a scope that permits access to retrieve the user's YouTube data. It uses incremental
authorization (include_granted_scopes=true
) to ensure that the new access token
covers any scopes to which the user previously granted the application access. Several other
parameters are also set in the example.
https://accounts.google.com/o/oauth2/v2/auth? scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fyoutube.force-ssl& access_type=offline& include_granted_scopes=true& response_type=code& state=state_parameter_passthrough_value& redirect_uri=http%3A%2F%2Flocalhost%2Foauth2callback& client_id=client_id
リクエスト URL を作成したら、ユーザーをその URL にリダイレクトします。
Google の OAuth 2.0 サーバーがユーザーを認証し、アプリケーションがリクエストされたスコープにアクセスすることについてユーザーの同意を得ます。レスポンスは、指定したリダイレクト URL を使用してアプリケーションに返されます。
ステップ 3: Google がユーザーに同意を求める
このステップでは、ユーザーはリクエストされたアクセス権をアプリケーションに付与するかどうかを決定します。この段階では、同意ウィンドウが表示され、アプリケーションの名前と、ユーザーの認証情報を使用してアクセス権限をリクエストする Google API サービスの名前と、付与されるアクセス権の範囲の概要が表示されます。ユーザーは、アプリケーションからリクエストされた 1 つ以上のスコープへのアクセスを許可することも、リクエストを拒否することもできます。
この段階でアプリケーションは、アクセスが許可されたかどうかを示す Google の OAuth 2.0 サーバーからのレスポンスを待機するため、何もする必要はありません。このレスポンスについては、次の手順で説明します。
エラー
Google の OAuth 2.0 認可エンドポイントへのリクエストでは、想定される認証フローと認可フローではなく、ユーザー向けのエラー メッセージが表示されることがあります。一般的なエラーコードと推奨解決策を以下に示します。
admin_policy_enforced
Google アカウントは、Google Workspace 管理者のポリシーにより、リクエストされた 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
OAuth クライアント シークレットが正しくありません。このリクエストで使用されているクライアント ID とシークレットなど、OAuth クライアントの構成を確認します。
invalid_grant
アクセス トークンを更新する場合、または増分認可を使用している場合、トークンの有効期限が切れているか、無効になった可能性があります。ユーザーを再度認証し、新しいトークンの取得についてユーザーの同意を求めます。このエラーが引き続き表示される場合は、アプリケーションが正しく構成され、リクエストで正しいトークンとパラメータを使用していることを確認してください。それ以外の場合は、ユーザー アカウントが削除されたか、無効になっている可能性があります。
redirect_uri_mismatch
承認リクエストで渡された redirect_uri
が、OAuth クライアント ID の承認済みのリダイレクト URI と一致しません。 Google API Console Credentials pageで承認済みのリダイレクト URI を確認します。
redirect_uri
パラメータは、非推奨となりサポートされなくなった OAuth 帯域外(OOB)フローを参照している可能性があります。統合を更新するには、移行ガイドをご覧ください。
invalid_request
リクエストに何か問題がありました。これには、さまざまな理由が考えられます。
- リクエストの形式が正しくありませんでした
- リクエストに必須パラメータがありません
- Google がサポートしていない認証方法がリクエストで使用されている。OAuth 統合で推奨の統合方法が使用されていることを確認する
ステップ 4: OAuth 2.0 サーバー レスポンスを処理する
OAuth 2.0 サーバーは、リクエストで指定された URL を使用して、アプリケーションのアクセス リクエストに応答します。
ユーザーがアクセス リクエストを承認すると、レスポンスに認証コードが含まれます。ユーザーがリクエストを承認しないと、レスポンスにエラー メッセージが含まれます。ウェブサーバーに返された認証コードまたはエラー メッセージは、次のようにクエリ文字列に表示されます。
エラー レスポンス:
https://oauth2.example.com/auth?error=access_denied
認証コードのレスポンス:
https://oauth2.example.com/auth?code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7
OAuth 2.0 サーバー レスポンスの例
このフローをテストするには、次のサンプル URL をクリックします。この URL は、Google ドライブのファイルのメタデータを表示するための読み取り専用権限をリクエストしています。
https://accounts.google.com/o/oauth2/v2/auth? scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fyoutube.force-ssl& access_type=offline& include_granted_scopes=true& response_type=code& state=state_parameter_passthrough_value& redirect_uri=http%3A%2F%2Flocalhost%2Foauth2callback& client_id=client_id
OAuth 2.0 フローが完了すると、http://localhost/oauth2callback
にリダイレクトされます。このアドレスでローカルマシンがファイルを提供しない限り、404 NOT FOUND
エラーが発生する可能性があります。次のステップでは、ユーザーがアプリケーションにリダイレクトされたときに URI で返される情報の詳細を提供します。
ステップ 5: 更新トークンとアクセス トークンの認証コードを交換する
ウェブサーバーは認証コードを受信すると、認証コードをアクセス トークンと交換できます。
PHP
認証コードをアクセス トークンと交換するには、authenticate
メソッドを使用します。
$client->authenticate($_GET['code']);
アクセス トークンは getAccessToken
メソッドを使用して取得できます。
$access_token = $client->getAccessToken();
Python
コールバック ページで、google-auth
ライブラリを使用して認可サーバーのレスポンスを確認します。次に、flow.fetch_token
メソッドを使用して、レスポンス内の認証コードをアクセス トークンと交換します。
state = flask.session['state'] flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file( 'client_secret.json', scopes=['https://www.googleapis.com/auth/youtube.force-ssl'], state=state) flow.redirect_uri = flask.url_for('oauth2callback', _external=True) authorization_response = flask.request.url flow.fetch_token(authorization_response=authorization_response) # Store the credentials in the session. # ACTION ITEM for developers: # Store user's access and refresh tokens in your data store if # incorporating this code into your real app. credentials = flow.credentials flask.session['credentials'] = { 'token': credentials.token, 'refresh_token': credentials.refresh_token, 'token_uri': credentials.token_uri, 'client_id': credentials.client_id, 'client_secret': credentials.client_secret, 'scopes': credentials.scopes}
Ruby
コールバック ページで、googleauth
ライブラリを使用して認可サーバーのレスポンスを確認します。authorizer.handle_auth_callback_deferred
メソッドを使用して認証コードを保存し、最初に承認をリクエストした URL にリダイレクトします。これにより、ユーザーのセッションに結果を一時的に保存することで、コードの交換を延期できます。
target_url = Google::Auth::WebUserAuthorizer.handle_auth_callback_deferred(request) redirect target_url
Node.js
認証コードをアクセス トークンと交換するには、getToken
メソッドを使用します。
const url = require('url'); // Receive the callback from Google's OAuth 2.0 server. app.get('/oauth2callback', async (req, res) => { let q = url.parse(req.url, true).query; if (q.error) { // An error response e.g. error=access_denied console.log('Error:' + q.error); } else if (q.state !== req.session.state) { //check state value console.log('State mismatch. Possible CSRF attack'); res.end('State mismatch. Possible CSRF attack'); } else { // Get access and refresh tokens (if access_type is offline) let { tokens } = await oauth2Client.getToken(q.code); oauth2Client.setCredentials(tokens); });
HTTP/REST
認証コードをアクセス トークンと交換するには、https://oauth2.googleapis.com/token
エンドポイントを呼び出して、次のパラメータを設定します。
フィールド | |
---|---|
client_id |
Credentials pageから取得した API Consoleクライアント ID。 |
client_secret |
Credentials pageから取得した API Consoleクライアント シークレット。 |
code |
最初のリクエストから返された認証コード。 |
grant_type |
OAuth 2.0 仕様で定義されている、このフィールドの値は authorization_code に設定する必要があります。 |
redirect_uri |
指定された client_id の API Console
Credentials page にあるプロジェクト用として一覧表示されたリダイレクト URI のいずれか。 |
次のスニペットはリクエストの例を示したものです。
POST /token HTTP/1.1 Host: oauth2.googleapis.com Content-Type: application/x-www-form-urlencoded code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7& client_id=your_client_id& client_secret=your_client_secret& redirect_uri=https%3A//oauth2.example.com/code& grant_type=authorization_code
Google は、このリクエストに応答して、有効期間の短いアクセス トークンと更新トークンを含む JSON オブジェクトを返します。更新トークンは、Google の承認サーバーへの最初のリクエストで、アプリケーションが access_type
パラメータを offline
に設定した場合にのみ返されます。
レスポンスには、次のフィールドが含まれます。
フィールド | |
---|---|
access_token |
Google API リクエストを承認するためにアプリケーションが送信するトークン。 |
expires_in |
アクセス トークンの残りの存続期間(秒)。 |
refresh_token |
新しいアクセス トークンを取得するために使用できるトークン。更新トークンは、ユーザーがアクセス権を取り消すまで有効です。
ここでも、このフィールドがレスポンスに含まれるのは、Google の承認サーバーへの最初のリクエストで access_type パラメータを offline に設定した場合のみです。
|
scope |
access_token で付与されるアクセス権のスコープ。スペース区切りの文字列(大文字と小文字を区別)のリストとして表されます。 |
token_type |
返されるトークンのタイプ。現在、このフィールドの値は常に Bearer に設定されます。 |
次のスニペットは、レスポンスの例を示しています。
{ "access_token": "1/fFAGRNJru1FTz70BzhT3Zg", "expires_in": 3920, "token_type": "Bearer", "scope": "https://www.googleapis.com/auth/youtube.force-ssl", "refresh_token": "1//xEoDL4iW3cxlI7yDbSRFYNG01kVKM2C-259HOF2aQbI" }
エラー
アクセス トークンの認証コードを交換すると、想定されるレスポンスではなく、次のエラーが発生することがあります。一般的なエラーコードと推奨される解決策を以下に示します。
invalid_grant
指定された認証コードが無効であるか、形式が正しくありません。OAuth プロセスを再起動して新しいコードをリクエストし、もう一度同意をユーザーに求めます。
Google API の呼び出し
PHP
次の手順で、アクセス トークンを使用して Google API を呼び出します。
- アクセス トークンを新しい
Google\Client
オブジェクトに適用する必要がある場合(アクセス トークンをユーザー セッションに保存した場合など)は、setAccessToken
メソッドを使用します。$client->setAccessToken($access_token);
- 呼び出す API のサービス オブジェクトを作成します。サービス オブジェクトを作成するには、呼び出す API のコンストラクタに承認済みの
Google\Client
オブジェクトを指定します。たとえば、YouTube Data API を呼び出すには、次のようにします。$youtube = new Google_Service_YouTube($client);
- サービス オブジェクトによって提供されるインターフェースを使用して、API サービスにリクエストを送信します。
たとえば、YouTube Data API を使用して、認証されたユーザーのチャンネルのライブ配信のリストを取得するには、次のようにします。
$broadcasts = $youtube->liveBroadcasts->listLiveBroadcasts('id,snippet', [ 'mine' => true ]);
Python
アクセス トークンを取得した後、アプリケーションはそのトークンを使用して、特定のユーザー アカウントまたはサービス アカウントに代わって API リクエストを認可できます。ユーザー固有の認証情報を使用して、呼び出す API のサービス オブジェクトを作成し、そのオブジェクトを使用して承認済みの API リクエストを行います。
- 呼び出す API のサービス オブジェクトを作成します。サービス オブジェクトを作成するには、API の名前とバージョン、およびユーザー認証情報を指定して、
googleapiclient.discovery
ライブラリのbuild
メソッドを呼び出します。 たとえば、YouTube Data API のバージョン 3 を呼び出すには、次のようにします。from googleapiclient.discovery import build youtube = build('youtube', 'v3', credentials=credentials)
- サービス オブジェクトによって提供されるインターフェースを使用して、API サービスにリクエストを送信します。
たとえば、YouTube Data API を使用して、認証されたユーザーのチャンネルのライブ配信のリストを取得するには、次のようにします。
broadcasts = youtube.liveBroadcasts().list(part='id,snippet', mine=True).execute()
Ruby
アクセス トークンを取得した後、アプリケーションはそのトークンを使用して、特定のユーザー アカウントまたはサービス アカウントに代わって API リクエストを行うことができます。ユーザー固有の認証情報を使用して、呼び出す API のサービス オブジェクトを作成し、そのオブジェクトを使用して承認済みの API リクエストを行います。
- 呼び出す API のサービス オブジェクトを作成します。
たとえば、YouTube Data API のバージョン 3 を呼び出すには、次のようにします。
youtube = Google::Apis::YoutubeV3::YouTubeService.new
- サービスの認証情報を設定します。
youtube.authorization = credentials
- サービス オブジェクトによって提供されるインターフェースを使用して、API サービスにリクエストを送信します。
たとえば、YouTube Data API を使用して、認証されたユーザーのチャンネルのライブ配信のリストを取得するには、次のようにします。
broadcasts = youtube.list_liveBroadcasts('id,snippet', mine: true)
または、メソッドに options
パラメータを指定して、メソッドごとに承認を提供することもできます。
broadcasts = youtube.list_liveBroadcasts('id,snippet', mine: true)
Node.js
アクセス トークンを取得して OAuth2
オブジェクトに設定したら、そのオブジェクトを使用して Google API を呼び出します。アプリケーションはこのトークンを使用して、特定のユーザー アカウントまたはサービス アカウントに代わって API リクエストを承認できます。呼び出す API のサービス オブジェクトを作成します。
const { google } = require('googleapis'); // Example of using Google Drive API to list filenames in user's Drive. const drive = google.drive('v3'); drive.files.list({ auth: oauth2Client, pageSize: 10, fields: 'nextPageToken, files(id, name)', }, (err1, res1) => { if (err1) return console.log('The API returned an error: ' + err1); const files = res1.data.files; if (files.length) { console.log('Files:'); files.map((file) => { console.log(`${file.name} (${file.id})`); }); } else { console.log('No files found.'); } });
HTTP/REST
アプリケーションがアクセス トークンを取得した後、API に必要なアクセス スコープが付与されていれば、そのトークンを使用して特定のユーザー アカウントに代わって Google API を呼び出すことができます。これを行うには、access_token
クエリ パラメータまたは Authorization
HTTP ヘッダーの Bearer
値を使用して、API へのリクエストにアクセス トークンを含めます。クエリ文字列はサーバーログに表示される傾向があるため、可能であれば HTTP ヘッダーの使用をおすすめします。ほとんどの場合、クライアント ライブラリを使用して Google API の呼び出しをセットアップできます(YouTube Live Streaming API の呼び出しなど)。
YouTube Live Streaming API は、サービス アカウントのフローをサポートしていません。サービス アカウントを YouTube アカウントにリンクする方法はないため、このフローでリクエストを承認しようとすると、NoLinkedYouTubeAccount
エラーが発生します。
OAuth 2.0 Playground では、すべての Google API を試して、スコープを確認できます。
HTTP GET の例
Authorization: Bearer
HTTP ヘッダーを使用して
liveBroadcasts.list
エンドポイント(YouTube Live Streaming API)を呼び出すと、次のようになります。独自のアクセス トークンを指定する必要があります。
GET /youtube/v3/liveBroadcasts?part=id%2Csnippet&mine=true HTTP/1.1 Host: www.googleapis.com Authorization: Bearer access_token
次に、access_token
クエリ文字列パラメータを使用して、認証されたユーザーに対して同じ API を呼び出します。
GET https://www.googleapis.com/youtube/v3/liveBroadcasts?access_token=access_token&part=id%2Csnippet&mine=true
curl
の例
これらのコマンドは、curl
コマンドライン アプリケーションを使用してテストできます。HTTP ヘッダー オプションを使用する例を次に示します(推奨)。
curl -H "Authorization: Bearer access_token" https://www.googleapis.com/youtube/v3/liveBroadcasts?part=id%2Csnippet&mine=true
または、クエリ文字列パラメータ オプションを次のように指定します。
curl https://www.googleapis.com/youtube/v3/liveBroadcasts?access_token=access_token&part=id%2Csnippet&mine=true
完全なサンプルコード
次の例では、ユーザーがアプリケーションにデータの取得を承認した後、認証されたユーザーの YouTube チャンネルのライブ配信を表示する JSON 形式のオブジェクトを出力します。
PHP
この例を実行するには:
- API Consoleで、リダイレクト URL のリストにローカルマシンの URL を追加します(例:
http://localhost:8080
)。 - 新しいディレクトリを作成し、そのディレクトリに移動します。例:
mkdir ~/php-oauth2-example cd ~/php-oauth2-example
- Composer を使用して PHP 用の Google API クライアント ライブラリをインストールします。
composer require google/apiclient:^2.10
- 以下の内容で
index.php
ファイルとoauth2callback.php
ファイルを作成します。 - PHP を提供するように構成されたウェブサーバーで、サンプルを実行します。PHP 5.6 以降を使用している場合は、PHP の組み込みテスト ウェブサーバー(
php -S localhost:8080 ~/php-oauth2-example
)を使用できます。
index.php
<?php require_once __DIR__.'/vendor/autoload.php'; session_start(); $client = new Google\Client(); $client->setAuthConfig('client_secrets.json'); $client->addScope(Google_Service_YouTube::YOUTUBE_FORCE_SSL); if (isset($_SESSION['access_token']) && $_SESSION['access_token']) { $client->setAccessToken($_SESSION['access_token']); $youtube = new Google_Service_YouTube($client); $broadcasts = $youtube->liveBroadcasts->listLiveBroadcasts('id,snippet', [ 'mine' => true ]); echo json_encode($broadcasts); } else { $redirect_uri = 'http://' . $_SERVER['HTTP_HOST'] . '/oauth2callback.php'; header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL)); }
oauth2callback.php
<?php require_once __DIR__.'/vendor/autoload.php'; session_start(); $client = new Google\Client(); $client->setAuthConfigFile('client_secrets.json'); $client->setRedirectUri('http://' . $_SERVER['HTTP_HOST'] . '/oauth2callback.php'); $client->addScope(Google_Service_YouTube::YOUTUBE_FORCE_SSL); if (! isset($_GET['code'])) { // Generate and set state value $state = bin2hex(random_bytes(16)); $client->setState($state); $_SESSION['state'] = $state; $auth_url = $client->createAuthUrl(); header('Location: ' . filter_var($auth_url, FILTER_SANITIZE_URL)); } else { // Check the state value if (!isset($_GET['state']) || $_GET['state'] !== $_SESSION['state']) { die('State mismatch. Possible CSRF attack.'); } $client->authenticate($_GET['code']); $_SESSION['access_token'] = $client->getAccessToken(); $redirect_uri = 'http://' . $_SERVER['HTTP_HOST'] . '/'; header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL)); }
Python
この例では、Flask フレームワークを使用します。http://localhost:8080
でウェブ アプリケーションを実行し、OAuth 2.0 フローをテストできます。この URL に移動すると、次の 4 つのリンクが表示されます。
- API リクエストをテストする: このリンクは、サンプル API リクエストの実行を試みるページにリンクしています。必要に応じて、承認フローが開始されます。成功すると、ページに API レスポンスが表示されます。
- 認証フローを直接テストする: このリンクは、承認フローを介してユーザーにユーザーを誘導しようとするページにリンクします。アプリがユーザーに代わって承認済みの API リクエストを送信する権限をリクエストします。
- 現在の認証情報を取り消す: ユーザーがすでにアプリケーションに付与している権限を 取り消すページにリンクしています。
- Flask セッション認証情報を消去: このリンクは、Flask セッションに保存されている認証情報を消去します。これにより、すでにアプリに権限を付与していたユーザーが新しいセッションで API リクエストを実行しようとした場合にどうなるかがわかります。また、ユーザーがアプリに付与した権限をユーザーが取り消したにもかかわらず、アプリが取り消されたアクセス トークンでリクエストを認証しようとした場合にアプリが取得する API レスポンスも確認できます。
# -*- coding: utf-8 -*- import os import flask import requests import google.oauth2.credentials import google_auth_oauthlib.flow import googleapiclient.discovery # This variable specifies the name of a file that contains the OAuth 2.0 # information for this application, including its client_id and client_secret. CLIENT_SECRETS_FILE = "client_secret.json" # This OAuth 2.0 access scope allows for full read/write access to the # authenticated user's account and requires requests to use an SSL connection. SCOPES = ['https://www.googleapis.com/auth/youtube.force-ssl'] API_SERVICE_NAME = 'youtube' API_VERSION = 'v3' app = flask.Flask(__name__) # Note: A secret key is included in the sample so that it works. # If you use this code in your application, replace this with a truly secret # key. See https://flask.palletsprojects.com/quickstart/#sessions. app.secret_key = 'REPLACE ME - this value is here as a placeholder.' @app.route('/') def index(): return print_index_table() @app.route('/test') def test_api_request(): if 'credentials' not in flask.session: return flask.redirect('authorize') # Load credentials from the session. credentials = google.oauth2.credentials.Credentials( **flask.session['credentials']) youtube = googleapiclient.discovery.build( API_SERVICE_NAME, API_VERSION, credentials=credentials) broadcasts = youtube.liveBroadcasts().list(part='id,snippet', mine=True).execute() # Save credentials back to session in case access token was refreshed. # ACTION ITEM: In a production app, you likely want to save these # credentials in a persistent database instead. flask.session['credentials'] = credentials_to_dict(credentials) return flask.jsonify(**broadcasts) @app.route('/authorize') def authorize(): # Create flow instance to manage the OAuth 2.0 Authorization Grant Flow steps. flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file( CLIENT_SECRETS_FILE, scopes=SCOPES) # The URI created here must exactly match one of the authorized redirect URIs # for the OAuth 2.0 client, which you configured in the API Console. If this # value doesn't match an authorized URI, you will get a 'redirect_uri_mismatch' # error. flow.redirect_uri = flask.url_for('oauth2callback', _external=True) authorization_url, state = flow.authorization_url( # Enable offline access so that you can refresh an access token without # re-prompting the user for permission. Recommended for web server apps. access_type='offline', # Enable incremental authorization. Recommended as a best practice. include_granted_scopes='true') # Store the state so the callback can verify the auth server response. flask.session['state'] = state return flask.redirect(authorization_url) @app.route('/oauth2callback') def oauth2callback(): # Specify the state when creating the flow in the callback so that it can # verified in the authorization server response. state = flask.session['state'] flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file( CLIENT_SECRETS_FILE, scopes=SCOPES, state=state) flow.redirect_uri = flask.url_for('oauth2callback', _external=True) # Use the authorization server's response to fetch the OAuth 2.0 tokens. authorization_response = flask.request.url flow.fetch_token(authorization_response=authorization_response) # Store credentials in the session. # ACTION ITEM: In a production app, you likely want to save these # credentials in a persistent database instead. credentials = flow.credentials flask.session['credentials'] = credentials_to_dict(credentials) return flask.redirect(flask.url_for('test_api_request')) @app.route('/revoke') def revoke(): if 'credentials' not in flask.session: return ('You need to <a href="/authorize">authorize</a> before ' + 'testing the code to revoke credentials.') credentials = google.oauth2.credentials.Credentials( **flask.session['credentials']) revoke = requests.post('https://oauth2.googleapis.com/revoke', params={'token': credentials.token}, headers = {'content-type': 'application/x-www-form-urlencoded'}) status_code = getattr(revoke, 'status_code') if status_code == 200: return('Credentials successfully revoked.' + print_index_table()) else: return('An error occurred.' + print_index_table()) @app.route('/clear') def clear_credentials(): if 'credentials' in flask.session: del flask.session['credentials'] return ('Credentials have been cleared.<br><br>' + print_index_table()) def credentials_to_dict(credentials): return {'token': credentials.token, 'refresh_token': credentials.refresh_token, 'token_uri': credentials.token_uri, 'client_id': credentials.client_id, 'client_secret': credentials.client_secret, 'scopes': credentials.scopes} def print_index_table(): return ('<table>' + '<tr><td><a href="/test">Test an API request</a></td>' + '<td>Submit an API request and see a formatted JSON response. ' + ' Go through the authorization flow if there are no stored ' + ' credentials for the user.</td></tr>' + '<tr><td><a href="/authorize">Test the auth flow directly</a></td>' + '<td>Go directly to the authorization flow. If there are stored ' + ' credentials, you still might not be prompted to reauthorize ' + ' the application.</td></tr>' + '<tr><td><a href="/revoke">Revoke current credentials</a></td>' + '<td>Revoke the access token associated with the current user ' + ' session. After revoking credentials, if you go to the test ' + ' page, you should see an <code>invalid_grant</code> error.' + '</td></tr>' + '<tr><td><a href="/clear">Clear Flask session credentials</a></td>' + '<td>Clear the access token currently stored in the user session. ' + ' After clearing the token, if you <a href="/test">test the ' + ' API request</a> again, you should go back to the auth flow.' + '</td></tr></table>') if __name__ == '__main__': # When running locally, disable OAuthlib's HTTPs verification. # ACTION ITEM for developers: # When running in production *do not* leave this option enabled. os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1' # Specify a hostname and port that are set as a valid redirect URI # for your API project in the Google API Console. app.run('localhost', 8080, debug=True)
Ruby
この例では、Sinatra フレームワークを使用します。
require 'google/apis/youtube_v3' require 'sinatra' require 'googleauth' require 'googleauth/stores/redis_token_store' configure do enable :sessions set :client_id, Google::Auth::ClientId.from_file('/path/to/client_secret.json') set :scope, Google::Apis::DriveV3::AUTH_DRIVE_METADATA_READONLY set :token_store, Google::Auth::Stores::RedisTokenStore.new(redis: Redis.new) set :authorizer, Google::Auth::WebUserAuthorizer.new(settings.client_id, settings.scope, settings.token_store, '/oauth2callback') end get '/' do user_id = settings.client_id.id credentials = settings.authorizer.get_credentials(user_id, request) if credentials.nil? redirect settings.authorizer.get_authorization_url(login_hint: user_id, request: request) end youtube = Google::Apis::YoutubeV3::YouTubeService.new broadcasts = youtube.list_liveBroadcasts('id,snippet', mine: true) "<pre>#{JSON.pretty_generate(broadcasts.to_h)}</pre>" end get '/oauth2callback' do target_url = Google::Auth::WebUserAuthorizer.handle_auth_callback_deferred(request) redirect target_url end
Node.js
この例を実行するには:
-
API Consoleで、リダイレクト URL のリストにローカルマシンの URL を追加します(例:
http://localhost
)。 - メンテナンス LTS、アクティブな LTS、または Node.js の現在のリリースがインストールされていることを確認します。
-
新しいディレクトリを作成し、そのディレクトリに移動します。次に例を示します。
mkdir ~/nodejs-oauth2-example cd ~/nodejs-oauth2-example
-
Install the
Google API Client
Library
for Node.js using npm:
npm install googleapis
-
以下の内容で
main.js
ファイルを作成します。 -
例を実行します。
node .\main.js
main.js
const http = require('http'); const https = require('https'); const url = require('url'); const { google } = require('googleapis'); const crypto = require('crypto'); const express = require('express'); const session = require('express-session'); /** * To use OAuth2 authentication, we need access to a CLIENT_ID, CLIENT_SECRET, AND REDIRECT_URI. * To get these credentials for your application, visit * https://console.cloud.google.com/apis/credentials. */ const oauth2Client = new google.auth.OAuth2( YOUR_CLIENT_ID, YOUR_CLIENT_SECRET, YOUR_REDIRECT_URL ); // Access scopes for read-only Drive activity. const scopes = [ 'https://www.googleapis.com/auth/drive.metadata.readonly' ]; /* Global variable that stores user credential in this code example. * ACTION ITEM for developers: * Store user's refresh token in your data store if * incorporating this code into your real app. * For more information on handling refresh tokens, * see https://github.com/googleapis/google-api-nodejs-client#handling-refresh-tokens */ let userCredential = null; async function main() { const app = express(); app.use(session({ secret: 'your_secure_secret_key', // Replace with a strong secret resave: false, saveUninitialized: false, })); // Example on redirecting user to Google's OAuth 2.0 server. app.get('/', async (req, res) => { // Generate a secure random state value. const state = crypto.randomBytes(32).toString('hex'); // Store state in the session req.session.state = state; // Generate a url that asks permissions for the Drive activity scope const authorizationUrl = oauth2Client.generateAuthUrl({ // 'online' (default) or 'offline' (gets refresh_token) access_type: 'offline', /** Pass in the scopes array defined above. * Alternatively, if only one scope is needed, you can pass a scope URL as a string */ scope: scopes, // Enable incremental authorization. Recommended as a best practice. include_granted_scopes: true, // Include the state parameter to reduce the risk of CSRF attacks. state: state }); res.redirect(authorizationUrl); }); // Receive the callback from Google's OAuth 2.0 server. app.get('/oauth2callback', async (req, res) => { // Handle the OAuth 2.0 server response let q = url.parse(req.url, true).query; if (q.error) { // An error response e.g. error=access_denied console.log('Error:' + q.error); } else if (q.state !== req.session.state) { //check state value console.log('State mismatch. Possible CSRF attack'); res.end('State mismatch. Possible CSRF attack'); } else { // Get access and refresh tokens (if access_type is offline) let { tokens } = await oauth2Client.getToken(q.code); oauth2Client.setCredentials(tokens); /** Save credential to the global variable in case access token was refreshed. * ACTION ITEM: In a production app, you likely want to save the refresh token * in a secure persistent database instead. */ userCredential = tokens; // Example of using Google Drive API to list filenames in user's Drive. const drive = google.drive('v3'); drive.files.list({ auth: oauth2Client, pageSize: 10, fields: 'nextPageToken, files(id, name)', }, (err1, res1) => { if (err1) return console.log('The API returned an error: ' + err1); const files = res1.data.files; if (files.length) { console.log('Files:'); files.map((file) => { console.log(`${file.name} (${file.id})`); }); } else { console.log('No files found.'); } }); } }); // Example on revoking a token app.get('/revoke', async (req, res) => { // Build the string for the POST request let postData = "token=" + userCredential.access_token; // Options for POST request to Google's OAuth 2.0 server to revoke a token let postOptions = { host: 'oauth2.googleapis.com', port: '443', path: '/revoke', method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Content-Length': Buffer.byteLength(postData) } }; // Set up the request const postReq = https.request(postOptions, function (res) { res.setEncoding('utf8'); res.on('data', d => { console.log('Response: ' + d); }); }); postReq.on('error', error => { console.log(error) }); // Post the request with data postReq.write(postData); postReq.end(); }); const server = http.createServer(app); server.listen(80); } main().catch(console.error);
HTTP/REST
この Python の例では、Flask フレームワークと Requests ライブラリを使用して、OAuth 2.0 ウェブフローについて説明します。このフローには Python 用 Google API クライアント ライブラリを使用することをおすすめします。(Python タブの例ではクライアント ライブラリを使用しています)。
import json import flask import requests app = flask.Flask(__name__) CLIENT_ID = '123456789.apps.googleusercontent.com' CLIENT_SECRET = 'abc123' # Read from a file or environmental variable in a real app SCOPE = 'https://www.googleapis.com/auth/youtube.force-ssl' REDIRECT_URI = 'http://example.com/oauth2callback' @app.route('/') def index(): if 'credentials' not in flask.session: return flask.redirect(flask.url_for('oauth2callback')) credentials = json.loads(flask.session['credentials']) if credentials['expires_in'] <= 0: return flask.redirect(flask.url_for('oauth2callback')) else: headers = {'Authorization': 'Bearer {}'.format(credentials['access_token'])} req_uri = 'https://youtube.googleapis.com/youtube/v3/liveBroadcasts' r = requests.get(req_uri, headers=headers) return r.text @app.route('/oauth2callback') def oauth2callback(): if 'code' not in flask.request.args: state = str(uuid.uuid4()) flask.session['state'] = state auth_uri = ('https://accounts.google.com/o/oauth2/v2/auth?response_type=code' '&client_id={}&redirect_uri={}&scope={}&state={}').format(CLIENT_ID, REDIRECT_URI, SCOPE, state) return flask.redirect(auth_uri) else: if 'state' not in flask.request.args or flask.request.args['state'] != flask.session['state']: return 'State mismatch. Possible CSRF attack.', 400 auth_code = flask.request.args.get('code') data = {'code': auth_code, 'client_id': CLIENT_ID, 'client_secret': CLIENT_SECRET, 'redirect_uri': REDIRECT_URI, 'grant_type': 'authorization_code'} r = requests.post('https://oauth2.googleapis.com/token', data=data) flask.session['credentials'] = r.text return flask.redirect(flask.url_for('index')) if __name__ == '__main__': import uuid app.secret_key = str(uuid.uuid4()) app.debug = False app.run()
リダイレクト URI の検証ルール
Google では、デベロッパーがアプリケーションの安全性を確保できるように、リダイレクト URI に次の検証ルールを適用しています。リダイレクト URI は、以下のルールに準拠している必要があります。後述するドメイン、ホスト、パス、クエリ、スキーム、userinfo の定義については、RFC 3986 セクション 3 をご覧ください。
検証ルール | |
---|---|
スキーム |
リダイレクト URI には、プレーン HTTP ではなく HTTPS スキームを使用する必要があります。ローカルホスト URI(localhost IP アドレス URI を含む)は、このルールから除外されます。 |
ホスト |
ホストに raw IP アドレスを指定することはできません。ローカルホストの IP アドレスはこのルールから除外されます。 |
ドメイン |
“googleusercontent.com” にすることはできません。goo.gl など)を含めることはできません。また、短縮されたドメインを所有するアプリがそのドメインにリダイレクトすることを選択した場合、そのリダイレクト URI は、パスに “/google-callback/” を含めるか、末尾を “/google-callback” にする必要があります。 |
ユーザー情報 |
リダイレクト URI に userinfo サブコンポーネントを含めることはできません。 |
[パス] |
リダイレクト URI に、 |
クエリ |
リダイレクト URI にオープン リダイレクトを含めることはできません。 |
Fragment |
リダイレクト URI にフラグメント コンポーネントを含めることはできません。 |
文字数 |
リダイレクト URI には、次のような文字を含めることはできません。
|
段階的な承認
OAuth 2.0 プロトコルでは、アプリはスコープで識別されるリソースにアクセスするための承認をリクエストします。リソースが必要になったときに承認をリクエストするのが、ユーザー エクスペリエンスの最良のプラクティスと考えられています。この手法を実現するために、Google の承認サーバーは増分承認をサポートしています。この機能を使用すると、必要に応じてスコープをリクエストできます。ユーザーが新しいスコープの権限を付与すると、ユーザーがプロジェクトに許可したすべてのスコープを含むトークンと交換できる認証コードが返されます。
たとえば、アプリが認証されたユーザーの YouTube チャンネルのデータを取得し、ユーザーが特別なフローで YouTube アナリティクスのデータを取得できるようにするとします。この場合、ログイン時に、アプリは https://www.googleapis.com/auth/youtube.force-ssl
スコープへのアクセスのみをリクエストする可能性があります。ただし、ユーザーがチャンネルのアナリティクス データにアクセスしようとすると、アプリは https://www.googleapis.com/auth/yt-analytics.readonly
スコープへのアクセスをリクエストすることもできます。
段階的承認を実装するには、アクセス トークンをリクエストする通常のフローを完了しますが、以前に付与したスコープが認可リクエストに含まれていることを確認します。この方法により、アプリで複数のアクセス トークンを管理する必要がなくなります。
増分承認から取得したアクセス トークンには、次のルールが適用されます。
- このトークンを使用して、新しい組み合わせの承認に含まれるスコープのいずれかに対応するリソースにアクセスできます。
- 結合された認可の更新トークンを使用してアクセス トークンを取得する場合、アクセス トークンは結合された認可を表し、レスポンスに含まれる任意の
scope
値に使用できます。 - 統合された承認には、ユーザーが API プロジェクトに許可したすべてのスコープが含まれます。異なるクライアントから権限付与がリクエストされた場合も同様です。たとえば、ユーザーがアプリケーションのデスクトップ クライアントを使用して 1 つのスコープへのアクセスを許可し、その後モバイル クライアントを介して同じアプリケーションに別のスコープを付与した場合、統合された承認には両方のスコープが含まれることになります。
- 統合された承認を表すトークンを取り消すと、関連するユーザーに代わって、その承認のスコープに対するすべてのアクセス権が同時に取り消されます。
ステップ 1: 認証パラメータを設定するの言語固有のコードサンプルと、ステップ 2: Google の OAuth 2.0 サーバーへのリダイレクトのサンプル HTTP/REST リダイレクト URL では、すべて増分認証を使用しています。以下のコードサンプルには、増分認証を使用するために追加する必要があるコードも示されています。
PHP
$client->setIncludeGrantedScopes(true);
Python
Python では、include_granted_scopes
キーワード引数を true
に設定して、以前に付与したスコープが承認リクエストに含まれるようにします。次の例に示すように、設定するキーワード引数が include_granted_scopes
だけではない場合もあります。
authorization_url, state = flow.authorization_url( # Enable offline access so that you can refresh an access token without # re-prompting the user for permission. Recommended for web server apps. access_type='offline', # Enable incremental authorization. Recommended as a best practice. include_granted_scopes='true')
Ruby
auth_client.update!( :additional_parameters => {"include_granted_scopes" => "true"} )
Node.js
const authorizationUrl = oauth2Client.generateAuthUrl({ // 'online' (default) or 'offline' (gets refresh_token) access_type: 'offline', /** Pass in the scopes array defined above. * Alternatively, if only one scope is needed, you can pass a scope URL as a string */ scope: scopes, // Enable incremental authorization. Recommended as a best practice. include_granted_scopes: true });
HTTP/REST
この例では、呼び出し元のアプリケーションが、ユーザーの YouTube データを取得するためのアクセス権をリクエストしています。さらに、ユーザーがすでにアプリケーションに付与しているアクセス権もリクエストしています。
GET https://accounts.google.com/o/oauth2/v2/auth? scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fyoutube.force-ssl& access_type=offline& state=security_token%3D138rk%3Btarget_url%3Dhttp...index& redirect_uri=http%3A%2F%2Flocalhost%2Foauth2callback& response_type=code& client_id=client_id& include_granted_scopes=true
Refreshing an access token (offline access)
Access tokens periodically expire and become invalid credentials for a related API request. You can refresh an access token without prompting the user for permission (including when the user is not present) if you requested offline access to the scopes associated with the token.
- If you use a Google API Client Library, the client object refreshes the access token as needed as long as you configure that object for offline access.
- If you are not using a client library, you need to set the
access_type
HTTP query parameter tooffline
when redirecting the user to Google's OAuth 2.0 server. In that case, Google's authorization server returns a refresh token when you exchange an authorization code for an access token. Then, if the access token expires (or at any other time), you can use a refresh token to obtain a new access token.
Requesting offline access is a requirement for any application that needs to access a Google
API when the user is not present. For example, an app that performs backup services or
executes actions at predetermined times needs to be able to refresh its access token when the
user is not present. The default style of access is called online
.
Server-side web applications, installed applications, and devices all obtain refresh tokens during the authorization process. Refresh tokens are not typically used in client-side (JavaScript) web applications.
PHP
If your application needs offline access to a Google API, set the API client's access type to
offline
:
$client->setAccessType("offline");
ユーザーがリクエストしたスコープへのオフライン アクセスを許可した後、ユーザーがオフラインの場合でも、API クライアントを使用してユーザーの代わりに Google API にアクセスできます。クライアント オブジェクトは、必要に応じてアクセス トークンを更新します。
Python
Python では、access_type
キーワード引数を offline
に設定して、ユーザーに再度許可を求めることなくアクセス トークンを更新できるようにします。次の例に示すように、設定するキーワード引数が access_type
だけではない場合もあります。
authorization_url, state = flow.authorization_url( # Enable offline access so that you can refresh an access token without # re-prompting the user for permission. Recommended for web server apps. access_type='offline', # Enable incremental authorization. Recommended as a best practice. include_granted_scopes='true')
ユーザーがリクエストしたスコープへのオフライン アクセスを許可した後、ユーザーがオフラインの場合でも、API クライアントを使用してユーザーの代わりに Google API にアクセスできます。クライアント オブジェクトは、必要に応じてアクセス トークンを更新します。
Ruby
アプリケーションで Google API へのオフライン アクセスが必要な場合は、API クライアントのアクセスタイプを offline
に設定します。
auth_client.update!( :additional_parameters => {"access_type" => "offline"} )
ユーザーがリクエストしたスコープへのオフライン アクセスを許可した後、ユーザーがオフラインの場合でも、API クライアントを使用してユーザーの代わりに Google API にアクセスできます。クライアント オブジェクトは、必要に応じてアクセス トークンを更新します。
Node.js
アプリケーションで Google API へのオフライン アクセスが必要な場合は、API クライアントのアクセスタイプを offline
に設定します。
const authorizationUrl = oauth2Client.generateAuthUrl({ // 'online' (default) or 'offline' (gets refresh_token) access_type: 'offline', /** Pass in the scopes array defined above. * Alternatively, if only one scope is needed, you can pass a scope URL as a string */ scope: scopes, // Enable incremental authorization. Recommended as a best practice. include_granted_scopes: true });
ユーザーがリクエストしたスコープへのオフライン アクセスを許可した後、ユーザーがオフラインの場合でも、API クライアントを使用してユーザーの代わりに Google API にアクセスできます。クライアント オブジェクトは、必要に応じてアクセス トークンを更新します。
アクセス トークンには有効期限があります。アクセス トークンの有効期限が近づくと、このライブラリは自動的に更新トークンを使用して新しいアクセス トークンを取得します。トークン イベントを使用すると、常に最新のトークンが保存されるようになります。
oauth2Client.on('tokens', (tokens) => { if (tokens.refresh_token) { // store the refresh_token in your secure persistent database console.log(tokens.refresh_token); } console.log(tokens.access_token); });
このトークン イベントは最初の承認でのみ発生し、generateAuthUrl
メソッドを呼び出してリフレッシュ トークンを受け取るときに、access_type
を offline
に設定する必要があります。更新トークンを受信するための適切な制約を設定せずに、必要な権限をアプリにすでに付与している場合は、新しい更新トークンを受信するようにアプリケーションを再承認する必要があります。
後で refresh_token
を設定するには、setCredentials
メソッドを使用します。
oauth2Client.setCredentials({ refresh_token: `STORED_REFRESH_TOKEN` });
クライアントが更新トークンを取得すると、次回の API 呼び出しでアクセス トークンが自動的に取得され、更新されます。
HTTP/REST
アクセス トークンを更新するには、アプリケーションから Google の承認サーバー(https://oauth2.googleapis.com/token
)に、次のパラメータを含む HTTPS POST
リクエストを送信します。
フィールド | |
---|---|
client_id |
API Consoleから取得したクライアント ID。 |
client_secret |
API Consoleから取得したクライアント シークレット。 |
grant_type |
OAuth 2.0 仕様で定義されているように、このフィールドの値は refresh_token に設定する必要があります。 |
refresh_token |
認証コード交換から返された更新トークン。 |
次のスニペットはリクエストの例を示したものです。
POST /token HTTP/1.1 Host: oauth2.googleapis.com Content-Type: application/x-www-form-urlencoded client_id=your_client_id& client_secret=your_client_secret& refresh_token=refresh_token& grant_type=refresh_token
ユーザーがアプリケーションに付与されたアクセス権を取り消さない限り、トークン サーバーは新しいアクセス トークンを含む JSON オブジェクトを返します。次のスニペットは、レスポンスの例を示しています。
{ "access_token": "1/fFAGRNJru1FTz70BzhT3Zg", "expires_in": 3920, "scope": "https://www.googleapis.com/auth/drive.metadata.readonly", "token_type": "Bearer" }
発行される更新トークンの数には上限があります。つまり、クライアントとユーザーの組み合わせごとに 1 回、クライアント全体で 1 人のユーザーごとに 1 回という制限があります。更新トークンは長期にわたって保存し、有効な間は使い続ける必要があります。アプリケーションがリクエストする更新トークンが多すぎると、これらの制限に達することがあり、その場合は古い更新トークンが機能しなくなります。
トークンの取り消し
アプリケーションに付与したアクセス権の取り消しを希望する場合もあります。ユーザーは、 アカウント設定にアクセスしてアクセス権を取り消すことができます。詳しくは、アカウントにアクセスできるサードパーティのサイトやアプリの、サイトまたはアプリのアクセス権の削除のサポート ドキュメントをご覧ください。
また、アプリケーションに付与したアクセス権をプログラムで取り消すこともできます。プログラムによる取り消しは、ユーザーがアプリケーションの登録を解除した場合や、アプリケーションを削除した場合、またはアプリに必要な API リソースが大幅に変更された場合などに重要です。つまり、削除プロセスの一部に API リクエストを含めて、以前にアプリに付与された権限を確実に削除することができます。
PHP
プログラムでトークンを取り消すには、revokeToken()
を呼び出します。
$client->revokeToken();
Python
プログラムでトークンを取り消すには、トークンをパラメータとして含めて Content-Type
ヘッダーを設定する https://oauth2.googleapis.com/revoke
へのリクエストを行います。
requests.post('https://oauth2.googleapis.com/revoke', params={'token': credentials.token}, headers = {'content-type': 'application/x-www-form-urlencoded'})
Ruby
プログラムでトークンを取り消すには、oauth2.revoke
エンドポイントに HTTP リクエストを送信します。
uri = URI('https://oauth2.googleapis.com/revoke') response = Net::HTTP.post_form(uri, 'token' => auth_client.access_token)
トークンにはアクセス トークンまたは更新トークンを使用できます。トークンがアクセス トークンで、対応する更新トークンがある場合、更新トークンも取り消されます。
取り消しが正常に処理された場合、レスポンスのステータス コードは 200
です。エラー条件の場合、ステータス コード 400
がエラーコードとともに返されます。
Node.js
プログラムでトークンを取り消すには、/revoke
エンドポイントに対して HTTPS POST リクエストを行います。
const https = require('https'); // Build the string for the POST request let postData = "token=" + userCredential.access_token; // Options for POST request to Google's OAuth 2.0 server to revoke a token let postOptions = { host: 'oauth2.googleapis.com', port: '443', path: '/revoke', method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Content-Length': Buffer.byteLength(postData) } }; // Set up the request const postReq = https.request(postOptions, function (res) { res.setEncoding('utf8'); res.on('data', d => { console.log('Response: ' + d); }); }); postReq.on('error', error => { console.log(error) }); // Post the request with data postReq.write(postData); postReq.end();
トークン パラメータには、アクセス トークンまたは更新トークンを使用できます。トークンがアクセス トークンで、対応する更新トークンがある場合、更新トークンも取り消されます。
取り消しが正常に処理された場合、レスポンスのステータス コードは 200
です。エラー条件の場合、ステータス コード 400
がエラーコードとともに返されます。
HTTP/REST
プログラムでトークンを取り消すには、アプリケーションは 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
がエラーコードとともに返されます。