きめ細かい権限を処理する方法

概要

きめ細かい権限を使用することで、ユーザーは各アプリと共有するアカウント データをよりきめ細かく制御できます。より細かい制御、透明性、セキュリティを提供することで、ユーザーとデベロッパーの双方にメリットがあります。このガイドでは、きめ細かい権限を処理するためにアプリケーションを正しく更新するために必要な変更と手順について説明します。

きめ細かい権限とは

メールとカレンダーの両方のスコープをリクエストする生産性向上アプリを開発するとします。たとえば、ユーザーはアプリケーションを Google カレンダーでのみ使用し、Gmail では使用したくない場合があります。きめ細かい OAuth 権限を使用することで、ユーザーは Google カレンダーの権限のみ付与し、Gmail 以外の権限を付与できます。 ユーザーが特定のデータへのアクセスを許可することで、データの漏洩を最小限に抑え、信頼を高め、プライバシーを最優先しながらデジタルライフを管理できるようになります。このようなシナリオに対処できるようにアプリケーションを設計することが重要です。

ログイン以外のスコープが複数リクエストされた場合

ログイン スコープと非ログイン スコープ

ログイン スコープと非ログイン スコープの両方をリクエストするアプリでは、まずログイン スコープemailprofileopenid)の同意ページが表示されます。基本的な ID 情報(名前、メールアドレス、プロフィール写真)の共有に同意すると、ログイン以外のスコープの詳細な権限同意画面が表示されます。この場合、アプリケーションはユーザーによって付与されているスコープを確認する必要があります。リクエストされたすべてのスコープをユーザーが付与していると仮定することはできません。次の例では、ウェブ アプリケーションが 3 つのログイン スコープすべてと、Google ドライブの非ログイン スコープをリクエストします。ユーザーがログイン スコープに同意すると、Google ドライブの権限に関する詳細な権限同意画面がユーザーに表示されます。

ログイン スコープと非ログイン スコープ

ログインしていないスコープが複数ある

アプリケーションが複数の非ログイン スコープをリクエストすると、権限の詳細な同意画面がユーザーに表示されます。ユーザーは、アプリケーションとの共有を承認する権限を選択できます。ユーザーの Gmail メッセージと Google カレンダーのデータへのアクセスをリクエストする権限の詳細な同意画面の例を次に示します。

ログインしていないスコープが複数ある

ログイン スコープemailprofileopenid)のみをリクエストするアプリの場合、#inspect-your-application-codegranular 権限の同意画面は適用されません。ユーザーはログイン リクエスト全体を承認または拒否します。つまり、アプリケーションがログイン スコープ(1 つ、2 つ、または 3 つすべて)のみをリクエストする場合、詳細な権限同意画面は適用されません。

非ログイン スコープを 1 つリクエストするアプリの場合、権限に関する詳細な同意画面は適用されません。つまり、ユーザーがリクエスト全体を承認するか拒否するかのいずれかとなり、同意画面にチェックボックスはありません。次の表は、詳細な権限同意画面が表示されるタイミングをまとめたものです。

ログイン スコープの数 ログイン以外のスコープの数 権限の詳細な同意画面
1~3 0 該当なし
1~3 1+ 該当
0 1 該当なし
0 2+ 該当

アプリケーションが影響を受けるかどうかを判断する

権限のリクエストに Google OAuth 2.0 認可エンドポイントが使用されているすべてのセクションを入念に確認します。ユーザーに詳細な権限同意画面が表示されるため、複数のスコープをリクエストする場合は注意してください。このような場合は、ユーザーが一部のスコープのみを承認する場合に、コードが対処できることを確認してください。

アプリケーションが複数のスコープを使用しているかどうかを確認する方法

アプリのコードまたは送信ネットワーク呼び出しを調べて、アプリによる Google OAuth 2.0 承認リクエストによって、詳細な権限同意画面が表示されるかどうかを確認します。

アプリケーション コードを検査する

アプリケーション コード内で、Google OAuth 認可エンドポイントを呼び出してユーザーに権限をリクエストしているセクションを確認します。Google API クライアント ライブラリを使用している場合は、多くの場合、クライアント初期化手順でアプリケーションがリクエストするスコープを確認できます。次のセクションで、いくつか例を示します。アプリケーションが Google OAuth 2.0 を処理する際に使用する SDK のドキュメントを参照し、アプリケーションが影響を受けるかどうかを判断してください。また、以下の例に示すガイダンスを参考にしてください。

Google Identity Services

次の Google Identity Services JavaScript ライブラリのコード スニペットは、複数の非ログイン スコープで TokenClient を初期化します。ウェブアプリがユーザーに承認をリクエストすると、権限の詳細な同意画面が表示されます。

const client = google.accounts.oauth2.initTokenClient({
  client_id: 'YOUR_CLIENT_ID',
  scope: 'https://www.googleapis.com/auth/calendar.readonly \
  https://www.googleapis.com/auth/contacts.readonly',
  callback: (response) => {
    ...
  },
});

Python

次のコード スニペットでは、google-auth-oauthlib.flow モジュールを使用して認証リクエストを作成しています。scope パラメータには、ログイン以外の 2 つのスコープが含まれています。ウェブ アプリケーションがユーザーに承認をリクエストすると、権限の詳細な同意画面が表示されます。

import google.oauth2.credentials
import google_auth_oauthlib.flow

# Use the client_secret.json file to identify the application requesting
# authorization. The client ID (from that file) and access scopes are required.
flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
    'client_secret.json',
    scopes=['https://www.googleapis.com/auth/calendar.readonly',
                    'https://www.googleapis.com/auth/contacts.readonly'])

Node.js

次のコード スニペットは、google.auth.OAuth2 オブジェクトを作成します。このオブジェクトは、scope パラメータに 2 つの非ログイン スコープを含む承認リクエストのパラメータを定義します。ウェブアプリがユーザーに承認をリクエストすると、権限の詳細な同意画面が表示されます。

const {google} = require('googleapis');

/**
  * 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 Calendar and Contacts.
const scopes = [
  'https://www.googleapis.com/auth/calendar.readonly',
  'https://www.googleapis.com/auth/contacts.readonly']
];

// Generate a url that asks permissions
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 best practices.
  include_granted_scopes: true
});

発信ネットワーク呼び出しの検査

ネットワーク呼び出しを検査する方法は、アプリのクライアントのタイプによって異なります。

ネットワーク呼び出しの検査中に、Google OAuth 認可エンドポイントに送信されたリクエストを探し、scope パラメータを調べます。

これらの値により、詳細な権限の同意画面が表示されます。cause

  • scope パラメータには、ログイン スコープと非ログイン スコープが含まれます。

    次のサンプル リクエストには、ユーザーの Google ドライブ ファイルのメタデータを表示するための、3 つのログイン スコープすべてと 1 つの非ログイン スコープが含まれています。

    https://accounts.google.com/o/oauth2/v2/auth?
    access_type=offline&
    scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile%20openid%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive.metadata.readonly&
    include_granted_scopes=true&
    response_type=code&
    redirect_uri=YOUR_REDIRECT_URL&
    client_id=YOUR_CLIENT_ID
  • scope パラメータに、ログイン以外のスコープが複数含まれています。

    次のサンプル リクエストには、ユーザーの Google ドライブ メタデータを表示し、特定の Google ドライブ ファイルを管理するための、ログイン以外の 2 つのスコープが含まれています。

  • https://accounts.google.com/o/oauth2/v2/auth?
    access_type=offline&
    scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive.metadata.readonly%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive.file&
    include_granted_scopes=true&
    response_type=code&
    redirect_uri=YOUR_REDIRECT_URL&
    client_id=YOUR_CLIENT_ID

きめ細かい権限を処理するためのベスト プラクティス

アプリをアップデートしてきめ細かい権限を処理する必要があると判断した場合は、必要な変更をコードに加え、複数のスコープの同意を適切に処理する必要があります。determineすべてのアプリケーションは、次のベスト プラクティスに準拠する必要があります。

  1. Google API サービス: ユーザーデータに関するポリシー確認して、準拠していることを確認します。
  2. タスクに必要な特定のスコープをリクエストします。Google OAuth 2.0 ポリシーに準拠して、必要なスコープのみをリクエストする必要があります。アプリのコア機能に不可欠な場合を除き、ログイン時に複数のスコープを要求することは避けてください。複数のスコープをバンドルすると、特にアプリの機能になじみのないユーザーが初めて使用する場合、これらの権限の必要性を把握するのが難しくなる可能性があります。その結果、アラームが作動し、ユーザーがアプリをこれ以上利用できなくなる可能性があります。
  3. 承認リクエストを行う前に、理由をユーザーに提示します。アプリがリクエストされた権限を必要とする理由、ユーザーのデータをどのように扱うか、リクエストを承認するとユーザーが得られるメリットを明確に説明してください。Google の調査によると、こうした説明により、ユーザーの信頼とエンゲージメントが向上することがわかっています。
  4. アプリケーションがスコープをリクエストするときは常に増分承認使用し、複数のアクセス トークンを管理する手間を省きます。
  5. ユーザーが付与したスコープを確認します。複数のスコープを一度にリクエストする場合、ユーザーがアプリのリクエストですべてのスコープを付与できるとは限りません。アプリはユーザーによって付与されたスコープを常に確認し、関連する機能を無効にしてスコープの拒否に対処する必要があります。複数のスコープに対する同意の処理に関する Google OAuth 2.0 ポリシーに沿って、ユーザーがスコープを必要とする特定の機能を使用する意思を明確に示してからのみ、ユーザーに同意を求めるようにします。

きめ細かい権限を処理するようにアプリケーションを更新する

Android アプリ

Google OAuth 2.0 の操作に使用している SDK のドキュメントを参照し、ベスト プラクティスに基づいてきめ細かい権限を処理するようにアプリケーションを更新してください。

Play 開発者サービスの auth.api.signin SDK を使用して Google OAuth 2.0 を操作する場合は、requestPermissions 関数を使用して必要最小限のスコープ セットをリクエストし、hasPermissions 関数を使用して、ユーザーが付与したスコープを確認できます。

Chrome 拡張機能のアプリケーション

Google OAuth 2.0 を操作するには、ベスト プラクティスに沿って Chrome Identity API を使用してください。

次の例は、きめ細かい権限を適切に処理する方法を示しています。

manifest.json

このサンプルのマニフェスト ファイルでは、Chrome 拡張機能アプリケーションに対してログイン以外の 2 つのスコープを宣言しています。

{
  "name": "Example Chrome extension application",
  ...
  "permissions": [
      "identity"
    ],
  "oauth2" : {
      "client_id": "YOUR_CLIENT_ID",
      "scopes":["https://www.googleapis.com/auth/calendar.readonly",
                "https://www.googleapis.com/auth/contacts.readonly"]
  }
}

不適切なアプローチ

オール オア ゼロ

ユーザーがボタンをクリックして承認プロセスを開始します。このコード スニペットでは、manifest.json ファイルで指定された 2 つのスコープについて、「オール オア ナッシング」の同意画面がユーザーに表示されることを前提としています。ユーザーが付与したスコープの確認が行われません。

oauth.js

...
document.querySelector('button').addEventListener('click', function () {
  chrome.identity.getAuthToken({ interactive: true },
      function (token) {
          if (token === undefined) {
            // User didn't authorize both scopes.
            // Updating the UX and application accordingly
            ...
          } else {
            // User authorized both or one of the scopes.
            // It neglects to check which scopes users granted and assumes users granted all scopes.

            // Calling the APIs, etc.
            ...
          }
      });
});

正しいアプローチ

最小のスコープ

必要最小限のスコープセットを選択する

アプリケーションは、必要最小限のスコープのセットのみをリクエストする必要があります。タスクを完了するために必要なスコープは、アプリケーションで一度に 1 つずつリクエストすることをおすすめします。

この例では、manifest.json ファイルで宣言されている両方のスコープが、必要最小限のスコープセットであることを前提としています。oauth.js ファイルは、Chrome Identity API を使用して Google との認証プロセスを開始します。ユーザーがアプリケーションへの権限付与をより細かく制御できるように、 きめ細かい権限を有効にする必要があります。アプリケーションは、ユーザーが承認するスコープを確認することで、ユーザーからのレスポンスを適切に処理する必要があります。

oauth.js

...
document.querySelector('button').addEventListener('click', function () {
  chrome.identity.getAuthToken({ interactive: true, enableGranularPermissions: true },
      function (token, grantedScopes) {
          if (token === undefined) {
            // User didn't authorize any scope.
            // Updating the UX and application accordingly
            ...
          } else {
            // User authorized the request. Now, check which scopes were granted.
            if (grantedScopes.includes('https://www.googleapis.com/auth/calendar.readonly'))
            {
              // User authorized Calendar read permission.
              // Calling the APIs, etc.
              ...
            }
            else
            {
              // User didn't authorize Calendar read permission.
              // Update UX and application accordingly
              ...
            }

            if (grantedScopes.includes('https://www.googleapis.com/auth/contacts.readonly'))
            {
              // User authorized Contacts read permission.
              // Calling the APIs, etc.
              ...
            }
            else
            {
              // User didn't authorize Contacts read permission.
              // Update UX and application accordingly
              ...
            }
          }
      });
});

iOS、iPadOS、macOS のアプリケーション

Google OAuth 2.0 の操作に使用している SDK のドキュメントを参照し、ベスト プラクティスに基づいてきめ細かい権限を処理するようにアプリケーションを更新してください。

iOS および macOS 用の Google ログイン ライブラリを使用して Google OAuth 2.0 を操作する場合は、詳細な権限の処理に関するドキュメントをご覧ください。

ウェブ アプリケーション

Google OAuth 2.0 の操作に使用している SDK のドキュメントを参照し、ベスト プラクティスに基づいてきめ細かい権限を処理するようにアプリケーションを更新してください。

サーバーサイド(オフライン)アクセス

サーバーサイド(オフライン)アクセスモードでは、次のことを行う必要があります。
  • サーバーを起動し、認可コードを受信するために一般公開されているエンドポイントを定義します。
  • Google Cloud コンソールの Credentials page で、パブリック エンドポイントの リダイレクト URI を構成します。

次のコード スニペットは、2 つの非ログイン スコープをリクエストする NodeJS の例を示しています。ユーザーには、権限の詳細な同意画面が表示されます。

不適切なアプローチ

オール オア ゼロ

ユーザーは認可 URL にリダイレクトされます。このコード スニペットでは、scopes 配列で指定された 2 つのスコープについて、「オール オア ナッシング」の同意画面がユーザーに表示されていることを前提としています。ユーザーが付与したスコープの確認が行われません。

main.js

...
const oauth2Client = new google.auth.OAuth2(
  YOUR_CLIENT_ID,
  YOUR_CLIENT_SECRET,
  YOUR_REDIRECT_URL
);

// Access scopes for two non-Sign-In scopes - Google Calendar and Contacts
const scopes = [
  'https://www.googleapis.com/auth/contacts.readonly',
  'https://www.googleapis.com/auth/calendar.readonly'
];

// Generate a url that asks permissions for the Google Calendar and Contacts scopes
const authorizationUrl = oauth2Client.generateAuthUrl({
  // 'online' (default) or 'offline' (gets refresh_token)
  access_type: 'offline',
  // Pass in the scopes array defined above
  scope: scopes,
  // Enable incremental authorization. Recommended as best practices.
  include_granted_scopes: true
});

async function main() {
  const server = http.createServer(async function (req, res) {
    // Example on redirecting user to Google OAuth 2.0 server.
    if (req.url == '/') {
      res.writeHead(301, { "Location": authorizationUrl });
    }
    // Receive the callback from Google OAuth 2.0 server.
    if (req.url.startsWith('/oauth2callback')) {
      // Handle the Google OAuth 2.0 server response
      let q = url.parse(req.url, true).query;

      if (q.error) {
        // User didn't authorize both scopes.
        // Updating the UX and application accordingly
        ...
      } else {
        // User authorized both or one of the scopes.
        // It neglects to check which scopes users granted and assumes users granted all scopes.

        // Get access and refresh tokens (if access_type is offline)
        let { tokens } = await oauth2Client.getToken(q.code);
        // Calling the APIs, etc.
        ...
      }
    }
    res.end();
  }).listen(80);
}
正しいアプローチ

最小スコープ

必要最小限のスコープセットを選択する

アプリケーションは、必要最小限のスコープのセットのみをリクエストする必要があります。タスクを完了するために必要なスコープは、アプリケーションで一度に 1 つずつリクエストすることをおすすめします。アプリケーションがスコープをリクエストする場合は、複数のアクセス トークンを管理しなくても済むように、増分承認を使用する必要があります。

アプリが複数の非ログイン スコープをリクエストする必要がある場合は、リクエスト時に必ず増分承認を使用し、ユーザーに付与されたスコープを確認する必要があります。

この例では、アプリが適切に機能するには、指定された両方のスコープが必要であることを前提としています。ユーザーがアプリケーションへの権限付与をより細かく制御できるように、 きめ細かい権限を有効にする必要があります。アプリケーションは、ユーザーが承認したスコープを確認することで、ユーザーからのレスポンスを適切に処理する必要があります。

main.js

...
const oauth2Client = new google.auth.OAuth2(
  YOUR_CLIENT_ID,
  YOUR_CLIENT_SECRET,
  YOUR_REDIRECT_URL
);

// Access scopes for two non-Sign-In scopes - Google Calendar and Contacts
const scopes = [
  'https://www.googleapis.com/auth/contacts.readonly',
  'https://www.googleapis.com/auth/calendar.readonly'
];

// Generate a url that asks permissions for the Google Calendar and Contacts scopes
const authorizationUrl = oauth2Client.generateAuthUrl({
  // 'online' (default) or 'offline' (gets refresh_token)
  access_type: 'offline',
  // Pass in the scopes array defined above
  scope: scopes,
  // Enable incremental authorization. Recommended as best practices.
  include_granted_scopes: true,
  // Set to true to enable more granular permissions for Google OAuth 2.0 client IDs created before 2019.
  // No effect for newer Google OAuth 2.0 client IDs, since more granular permissions is always enabled for them.
  enable_granular_consent: true
});

async function main() {
  const server = http.createServer(async function (req, res) {
    // Redirect users to Google OAuth 2.0 server.
    if (req.url == '/') {
      res.writeHead(301, { "Location": authorizationUrl });
    }
    // Receive the callback from Google OAuth 2.0 server.
    if (req.url.startsWith('/oauth2callback')) {
      // Handle the Google OAuth 2.0 server response
      let q = url.parse(req.url, true).query;

      if (q.error) {
        // User didn't authorize both scopes.
        // Updating the UX and application accordingly
        ...
      } else {
        // Get access and refresh tokens (if access_type is offline)
        let { tokens } = await oauth2Client.getToken(q.code);
        oauth2Client.setCredentials(tokens);

        // User authorized the request. Now, check which scopes were granted.
        if (tokens.scope.includes('https://www.googleapis.com/auth/calendar.readonly'))
        {
          // User authorized Calendar read permission.
          // Calling the APIs, etc.
          ...
        }
        else
        {
          // User didn't authorize Calendar read permission.
          // Calling the APIs, etc.
          ...
        }

        // Check which scopes user granted the permission to application
        if (tokens.scope.includes('https://www.googleapis.com/auth/contacts.readonly'))
        {
          // User authorized Contacts read permission.
          // Calling the APIs, etc.
          ...
        }
        else
        {
          // User didn't authorize Contacts read permission.
          // Update UX and application accordingly
          ...
        }
      }
    }
    res.end();
  }).listen(80);
}

サーバーベースのアプリケーションから Google API にアクセスする方法については、 サーバーサイド ウェブアプリのガイドをご覧ください。

クライアントサイドのみのアクセス

  • Google Identity Services JavaScript ライブラリを使用して Google OAuth 2.0 とやり取りするアプリケーションについては、詳細な権限の処理に関するこちらのドキュメントをご覧ください。
  • JavaScript を使用して Google OAuth 2.0 認可エンドポイントを直接呼び出すアプリケーションについては、詳細な権限の処理に関するこちらのドキュメントをご覧ください。

詳細な権限の処理について、更新したアプリケーションをテストする

  1. ユーザーが権限リクエストに応答できるすべてのケースと、アプリで想定される動作を記述します。たとえば、リクエストした 3 つのスコープのうち 2 つをユーザーが承認する場合、アプリはそれに応じて動作する必要があります。
  2. 詳細な権限を有効にしてアプリケーションをテストします。きめ細かい権限を有効にするには、次の 2 つの方法があります。
    1. アプリケーションの OAuth 2.0 同意画面をチェックして、きめ細かい権限がアプリケーションに対してすでに有効になっているかどうかを確認します。詳細な権限が常に有効になっているため、テスト用に Google Cloud コンソールから新しいウェブ、Android、iOS の Google OAuth 2.0 クライアント ID を作成することもできます。
    2. Google OAuth の 認可エンドポイントを呼び出すときに、パラメータ enable_granular_consenttrue に設定します。一部の SDK はこのパラメータを明示的にサポートしています。それ以外のパラメータについては、ドキュメントを参照して、このパラメータと値を手動で追加する方法を確認してください。パラメータの追加が実装でサポートされていない場合は、前述のように、テスト目的でのみ、Google Cloud コンソールから新しいウェブ、Android、または iOS の Google OAuth 2.0 クライアント ID を作成できます。