如何处理精细权限

概览

通过精细权限,使用者可以更精细地控制哪些账号数据 选择与各个应用分享的内容它们能够为用户和开发者提供更优质的内容, 透明度和安全性。本指南将帮助您了解 变更和成功更新应用以处理精细权限的步骤。

什么是精细权限?

假设您开发一款办公应用,该应用同时请求电子邮件和日历作用域。您的用户 可能只想将您的应用程序用于 Google 日历,而不用于 Gmail。使用精细的 OAuth 权限,用户可以选择仅授予 Google 日历权限,而不授予 Gmail。 通过允许用户授予对特定数据的访问权限,这样可以最大限度地减少数据泄露,增强 信任,并支持用户以隐私保护为先,掌控其数字生活。请务必 设计您的应用以应对此类场景。

请求多个非登录范围时

登录和非登录范围

对于同时请求登录和非登录范围的应用,用户首先会看到同意声明 登录范围页面 (emailprofileopenid)。在用户同意 分享其基本身份信息(姓名、电子邮件地址和个人资料照片),用户将看到 针对非登录范围的精细权限同意屏幕。在这种情况下,应用 必须检查用户授予了哪些范围,而不能假定用户授予了所请求的全部权限 范围。在以下示例中,该 Web 应用请求所有三种登录权限范围以及 Google 云端硬盘非登录权限范围。在用户同意登录范围后,用户将看到 精细的权限同意屏幕:

登录和非登录范围

多个非登录范围

当应用请求更多权限时,系统会向用户显示精细的权限请求页面 多个非登录范围。用户可以选择他们想要批准哪些权限进行共享 应用下面是一个精细的权限同意屏幕示例, 访问用户的 Gmail 邮件和 Google 日历数据:

多个非登录范围

对于仅请求登录范围emailprofileopenid)的应用,精细权限意见征求界面不适用。用户批准或拒绝整个登录 请求。换句话说,如果应用仅请求登录范围(一、两个或全部) 则精细的权限同意屏幕不适用。

对于仅请求一项非登录范围的应用,适用细化权限意见征求界面。换言之,用户选择接受或拒绝 拒绝整个请求,且同意屏幕中没有任何复选框。下表 汇总了精细权限同意屏幕的显示时间。

登录范围的数量 非登录范围的数量 精细权限意见征求界面
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 服务

以下 Google Identity 服务 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 参数包含两个 非登录范围。在访问网站时,系统会显示精细的权限同意屏幕 来请求用户授权。

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 参数包含两个 非登录范围。当网站应用向用户请求授权时,系统会显示精细权限同意屏幕。

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 参数。

使用这些值后,系统即会显示精细的权限同意屏幕。

  • scope 参数包含登录权限范围和非登录权限范围。

    以下示例请求包含所有三个登录范围和一个非登录范围 查看用户的 Google 云端硬盘文件的元数据:

    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 云端硬盘文件:

  • 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

处理精细权限的最佳实践

如果您确定您的应用需要更新才能处理 那么您应该对代码进行必要的更新,以正确处理用户意见 多个范围。所有应用都应遵循以下最佳实践:

  1. 查看 Google API 服务:用户数据政策,确保您遵守相关政策。
  2. 请求任务所需的特定范围。您 必须遵守 Google OAuth 2.0 政策, 只请求您 需求。您应该避免要求提供多个 作用域,除非这对应用的核心功能来说必不可少。分类显示 多个作用域,特别是对于不熟悉您的 可能会使他们难以理解对这些功能的需求, 权限。这可能会引起警报,并阻止用户进一步与您的应用互动。
  3. 在发出授权请求之前,请向用户提供理由。明确说明您的应用为何需要所请求的权限, 您将如何处理用户数据,以及用户如何通过批准请求获益。 我们的研究表明,这些说明可以提高用户的信任度和互动度。
  4. 使用 增量授权
  5. 检查用户授予了哪些范围。在请求多个 范围,用户可能无法授予应用请求的所有范围。您的应用应始终 查看用户授予了哪些作用域,并通过停用相关作用域 功能。遵守 Google OAuth 2.0 政策 处理多项同意声明 范围,并且仅在用户明确表明同意情况后再提示用户进行同意 使用需要作用域的特定功能的 intent。

更新您的应用以处理精细权限

Android 应用

您应该查阅用来与 Google OAuth 2.0 和 更新应用以处理精细的权限 最佳实践

如果您使用 auth.api.signin Play 服务 SDK 与 Google OAuth 2.0 进行交互,您可以使用 requestPermissions 函数来请求所需的最小范围, 和 hasPermissions 函数来检查,将用户授权的范围 请求细化的权限。

Chrome 扩展程序应用

您应该使用 Chrome 浏览器 Identity API,可与 Google OAuth 2.0 搭配使用, 最佳实践

以下示例展示了如何正确处理精细权限。

manifest.json

示例清单文件为 Chrome 扩展程序声明两个非登录范围 应用。

{
  "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 文件中。它不会检查用户授予了哪些范围。

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

正确方法

最小范围

选择所需的最小范围集

应用应仅请求所需的最小一组范围。建议 在需要完成任务时,您的应用一次请求一个范围。

在此示例中,我们假定 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 进行交互,请查看 有关精细处理的文档 权限。

Web 应用

您应参阅用于与 Google OAuth 2.0 交互的 SDK 的文档,并根据最佳实践更新应用以处理精细权限。

服务器端(离线)访问

服务器端(离线)访问模式要求您执行以下操作:

以下代码段展示了一个 NodeJS 示例请求两个非登录范围。用户将 查看精细的权限同意屏幕。

方法不正确

全部或全无

用户会被重定向到授权网址。该代码段假定向用户显示 采用“一刀切”式的策略中所指定的两个范围的同意屏幕 scopes 段落。它不会检查用户授予了哪些范围。

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);
}
正确方法

最小范围

选择所需的最小范围集

应用应仅请求所需的最小范围集。建议您的应用在需要完成任务时一次请求一个镜重。每当您的应用请求作用域时,都应使用 增量授权 不必管理多个访问令牌

如果您的应用必须请求多个非登录作用域,您应该始终使用 增量授权 查看用户授权的范围。

在本示例中,假定声明的两个范围都是应用 正常运行。您应该选择启用 启用精细权限,以便用户可以更好地控制对您的系统授予权限 应用。您的应用应通过检查 应用已授权的范围

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

查看 服务器端 Web 应用指南

仅客户端访问权限

  • 对于使用 Google Identity 服务的应用 与 Google OAuth 2.0 进行交互的 JavaScript 库,您应该查看 文档 到这里就结束了
  • 对于使用 JavaScript 直接向 Google OAuth 2.0 授权端点发出调用的应用,您应参阅有关处理精细权限的文档

在处理精细权限方面测试更新后的应用

  1. 概述用户可以响应权限请求的所有情形以及 应用的预期行为例如,如果用户只向 请求范围,您的应用应有相应的行为。
  2. 在启用精细权限的情况下测试您的应用。您可以通过以下两种方式 细化权限:
    1. 检查应用的 OAuth 2.0 权限请求页面,确认 细化权限已启用, 应用。您还可以创建新的 Web、Android 或 iOS Google OAuth 2.0 客户端 ID 通过 Google Cloud 控制台进行测试,因为精细权限始终 为它们启用
    2. 设置参数 调用 Google OAuth 时将 enable_granular_consent 设置为 true 授权端点。某些 SDK 明确支持此参数。对于其他配置,请参阅相关文档,了解如何添加此参数并 手动设置其值 如果您的实现不支持添加该参数,您可以通过 Google Cloud 控制台创建新的 Web、Android 或 iOS Google OAuth 2.0 客户端 ID,仅出于测试目的(如上文所述)。
  3. 测试更新的应用时,请使用个人 Google 账号 (@gmail.com),而不要使用 拥有 Workspace 账号。这是因为 Workspace 企业版应用 全网域授权或标记为 可信 不受精细权限变更的影响。因此,使用工作区进行测试 账号可能无法按预期显示新的精细同意屏幕。