概览
通过精细的权限,用户可以更精细地控制选择与每个应用分享的账号数据。它们可以提供更高的控制力、透明度和安全性,让用户和开发者都受益。本指南将帮助您了解必要的更改和步骤,以成功更新应用以处理精细的权限。
什么是精细权限?
假设您要开发一款效率应用,需要同时请求电子邮件和日历作用域。您的用户可能希望只针对 Google 日历使用您的应用,而不使用 Gmail。通过精细的 OAuth 权限,用户可以选择仅授予 Google 日历权限,而不授予 Gmail 权限。通过允许用户授予对特定数据的访问权限,可以最大限度地减少数据泄露、增进信任,并让用户能够以隐私保护为先的掌控其数字生活。请务必设计您的应用以处理此类情况。
请求多个非登录范围时
登录范围和非登录范围
对于同时请求登录范围和非登录范围的应用,用户首先会看到登录范围(email
、profile
和 openid
)的同意页面。在用户同意共享其基本身份信息(姓名、电子邮件地址和个人资料照片)后,用户将看到针对非登录范围的精细权限许可屏幕。在这种情况下,应用必须检查用户授予的范围,不能假定用户授予了请求的所有范围。在以下示例中,Web 应用请求所有三个登录范围和一个 Google 云端硬盘非登录范围。在用户同意登录范围后,用户将看到针对 Google 云端硬盘权限的精细权限同意屏幕:
![登录范围和非登录范围](https://developers-dot-devsite-v2-prod.appspot.com/static/identity/protocols/oauth2/images/examples/identity-and-nonidentity.png?authuser=6&hl=zh-cn)
多个非登录范围
当应用请求多个非登录范围时,系统会向用户显示精细的权限同意屏幕。用户可以选择想要批准与应用共享的权限。以下是一个精细权限同意屏幕示例,请求访问用户的 Gmail 邮件和 Google 日历数据:
![多个非登录范围](https://developers-dot-devsite-v2-prod.appspot.com/static/identity/protocols/oauth2/images/examples/two-nonidentity.png?authuser=6&hl=zh-cn)
对于仅请求登录范围(email
、profile
和 openid
)的应用,精细权限同意屏幕不适用。用户可以批准或拒绝整个登录请求。换句话说,如果应用仅请求登录范围(一个、两个或全部三个),则精细权限同意屏幕将不适用。
对于仅请求一个非登录范围的应用,精细权限同意屏幕并不适用。换言之,用户可以批准或拒绝整个请求,并且权限请求页面中并没有显示复选框。下表汇总了精细权限许可界面的显示时间。
登录范围的数量 | 非登录范围的数量 | 精细权限同意屏幕 |
---|---|---|
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
。当 Web 应用向用户请求授权时,系统将显示精细权限同意屏幕。
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
参数包含两个非登录范围。当 Web 应用向用户请求授权时,系统将显示精细权限同意屏幕。
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
参数包含两个非登录范围。当 Web 应用向用户请求授权时,系统会显示精细的权限同意屏幕。
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 });
检查去电网络通话
- Web 应用 - 检查 Chrome 上的网络活动
- Android - 使用 Network Inspector 检查网络流量
-
Chrome 应用
- 前往 Chrome 扩展程序页面
- 选中扩展程序页面右上角的开发者模式复选框
- 选择要监控的扩展程序
- 点击扩展程序页面的 Inspect views 部分中的后台网页链接
- 此时会打开一个开发者工具弹出式窗口,您可以在其中通过 “网络”标签页监控网络流量
- iOS - 使用插桩分析 HTTP 流量
- Universal Windows Platform (UWP) - 在 Visual Studio 中检查网络流量
- 桌面应用 - 使用适用于应用开发的操作系统的 网络捕获工具
检查网络调用时,请查找发送到 Google OAuth 授权端点的请求并检查 scope
参数。
这些值cause导致显示精细的权限同意屏幕。
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
处理精细权限的最佳做法
如果您determine需要更新应用来处理精细权限,则应对代码进行必要的更新,以正确处理多个范围的同意情况。所有应用都应遵循以下最佳实践:
- 查看 Google API 服务:用户数据政策,确保您遵守其中的规定。
- 请求任务所需的特定范围。您必须遵守 Google OAuth 2.0 政策,您仅请求所需的范围。您应该避免在登录时请求多个作用域,除非这对实现应用的核心功能至关重要。将多个作用域捆绑在一起(尤其是对于不熟悉应用功能的首次用户而言),可能会使他们难以理解对这些权限的需求。这可能会引发闹钟提醒,并阻碍用户进一步与您的应用互动。
- 在发出授权请求之前向用户提供理由。明确说明您的应用为何需要所请求的权限、您将如何处理用户数据,以及批准该请求将为用户带来哪些好处。 我们的研究表明,这些说明可以提高用户信任度和互动度。
- 如果您的应用请求范围,请使用 增量授权,这样就不必管理多个访问令牌。
- 查看用户授予了哪些范围。在一次请求多个范围时,用户可能不会授予应用请求的所有范围。您的应用应始终检查用户授予了哪些作用域,并通过停用相关功能来处理任何作用域拒绝事件。遵循关于处理多个范围的同意情况的 Google OAuth 2.0 政策,并且仅在用户明确表示有意使用需要该范围的特定功能时,才再次提示用户。
更新您的应用以处理精细权限
Android 应用
您应参阅用于与 Google OAuth 2.0 交互的 SDK 的文档,并根据最佳实践更新您的应用以处理精细权限。
如果您使用 Play 服务中的 auth.api.signin 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 的文档,并根据最佳实践更新您的应用以处理精细权限。
服务器端(离线)访问
- 建立服务器并定义一个可公开访问的端点来接收授权代码。
- 在 Google Cloud 控制台的 Credentials page 中配置公共端点的 重定向 URI 。
以下代码段展示了一个 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 API。
仅客户端访问
- 对于使用 Google Identity 服务 JavaScript 库与 Google OAuth 2.0 进行交互的应用,您应查看此文档,了解如何处理精细权限。
- 对于使用 JavaScript 直接调用 Google OAuth 2.0 授权端点的应用,应查看此文档,了解如何处理精细权限。
测试更新后的应用如何处理精细权限
- 概述用户可以响应权限请求的所有情况以及应用的预期行为。例如,如果用户只授权了所请求三个范围中的两个,则应用应采取相应的行为。
-
在启用精细权限的情况下测试您的应用。您可以通过以下两种方式启用精细权限:
- 检查应用的 OAuth 2.0 同意屏幕,查看是否已为您的应用启用细化权限。您还可以出于测试目的,通过 Google Cloud 控制台创建新的 Web、Android 或 iOS Google OAuth 2.0 客户端 ID,因为系统始终会为这些客户端启用精细权限。
-
调用 Google OAuth
授权端点时,请将参数
enable_granular_consent
设置为true
。某些 SDK 明确支持此参数。对于其他参数,请参阅相关文档,了解如何手动添加此参数及其值。 如果您的实现不支持添加该参数,则可以通过 Google Cloud 控制台创建一个新的 Web、Android 或 iOS 版 Google OAuth 2.0 客户端 ID,仅用于测试目的(如前所述)。
- 测试更新后的应用时,请使用个人 Google 帐号 (@gmail.com),而不是 Workspace 帐号。这是因为,具有全网域授权或标记为受信任的 Workspace 企业版应用目前不受精细权限更改的影响。因此,使用贵组织的 Workspace 帐号进行测试时,可能不会按预期显示新的精细同意屏幕。