總覽
透過精細的權限,消費者可以更精細地控管要提供給每個應用程式的帳戶資料。這項功能可讓使用者更有效地控管設定,並提供更公開透明的資訊與安全性,因此能同時造福使用者和開發人員。本指南將協助您瞭解必要的變更,以及如何成功更新應用程式以處理精細的權限。
什麼是精細權限?
假設您開發的效率提升應用程式會同時要求電子郵件和日曆範圍。您的使用者可能只想將您的應用程式用於 Google 日曆,而不用於 Gmail。透過精細的 OAuth 權限,使用者可以選擇只授予 Google 日曆權限,無法授予 Gmail 權限。如果讓使用者授予特定資料的存取權,將能盡量降低資料外洩、強化信任感,並讓使用者得以控管自己的數位生活。請務必設計應用程式來處理這類情況。
要求多個非登入範圍時
登入範圍和非登入範圍
如果應用程式要求同時要求登入和非登入範圍,使用者會先看到登入範圍 (email
、profile
和 openid
) 的同意頁面。使用者同意分享基本身分識別資訊 (姓名、電子郵件地址和個人資料相片) 後,系統會針對非登入範圍顯示精細的權限同意畫面。在這種情況下,應用程式必須檢查使用者授予哪些範圍,而且不得假設使用者授予所有要求的範圍。在以下範例中,網頁應用程式要求所有三個登入範圍,以及 Google 雲端硬碟非登入範圍。使用者同意登入範圍後,使用者會看到 Google 雲端硬碟權限的精細權限同意畫面:
![登入範圍和非登入範圍](https://developers-dot-devsite-v2-prod.appspot.com/static/identity/protocols/oauth2/images/examples/identity-and-nonidentity.png?authuser=2&hl=zh-tw)
多個非登入範圍
當應用程式要求多個非登入範圍時,系統會向使用者顯示詳細的權限同意畫面。使用者可以選取要核准與應用程式共用的權限。以下是要求存取使用者 Gmail 郵件和 Google 日曆資料的精細權限同意畫面範例:
![多個非登入範圍](https://developers-dot-devsite-v2-prod.appspot.com/static/identity/protocols/oauth2/images/examples/two-nonidentity.png?authuser=2&hl=zh-tw)
如果應用程式僅要求登入範圍 (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
。如果網頁應用程式向使用者要求授權,系統就會顯示詳細的權限同意畫面。
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 });
檢查撥出網路呼叫
- 網頁應用程式: 查看 Chrome 的網路活動
- Android: 使用網路檢查器檢查網路流量
-
Chrome 應用程式
- 前往 Chrome 擴充功能頁面
- 勾選擴充功能頁面右上角的「開發人員模式」核取方塊
- 選取要監控的擴充功能
- 在擴充功能頁面的「檢查檢視畫面」部分中,按一下「背景頁面」連結
- 系統會開啟「開發人員工具」彈出式視窗,方便您在 「網路」分頁中監控網路流量
- iOS: 使用檢測設備分析 HTTP 流量
- 通用 Windows 平台 (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 互動,請參閱處理精細權限的說明文件。
網頁應用程式
建議您參閱與 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); }
查看 伺服器端網頁應用程式指南,瞭解如何透過伺服器應用程式存取 Google API。
僅限用戶端存取權
- 如果應用程式使用 Google Identity 服務 JavaScript 程式庫與 Google OAuth 2.0 互動,請參閱這份說明文件,瞭解如何處理精細的權限。
- 如果應用程式使用 JavaScript 直接呼叫 Google OAuth 2.0 授權端點,請參閱這份說明文件,瞭解如何處理精細的權限。
測試更新後的應用程式,以便處理精細的權限
- Outline 讓使用者回應權限要求的所有情況,以及應用程式的預期行為。舉例來說,如果使用者只授權三個要求範圍內的兩個權限,應用程式應相應地執行操作。
-
在啟用精細權限的情況下測試您的應用程式。您可以透過以下兩種方式啟用精細的權限:
- 檢查應用程式的 OAuth 2.0 同意畫面,確認應用程式是否已啟用精細權限。您也可以透過 Google Cloud 控制台建立新的 Web、Android 或 iOS Google OAuth 2.0 用戶端 ID 以進行測試,因為系統一律會啟用精細權限。
-
在呼叫 Google OAuth
授權端點時,將
enable_granular_consent
參數設為true
。部分 SDK 可明確支援這個參數。如需其他參數,請參閱說明文件,瞭解如何手動新增這個參數和參數值。如果您的實作不支援新增參數,您可以透過 Google Cloud 控制台建立新的網頁、Android 或 iOS Google OAuth 2.0 用戶端 ID,以便進行測試 (如前述說明所述)。
- 測試更新的應用程式時,請使用個人 Google 帳戶 (@gmail.com),而非 Workspace 帳戶。這是因為目前精細權限的變更,不會影響具全網域授權委派或標示為可信任的 Workspace Enterprise 應用程式。因此,使用貴機構的 Workspace 帳戶進行測試時,可能不會如預期顯示新的精細同意畫面。