使用使用者驗證的 Chat 應用程式必須支援精細 OAuth 權限,讓使用者授予部分要求的範圍。舉例來說,使用者可能會授予存取名稱的權限,但拒絕存取日曆。
處理精細 OAuth 權限的方式,取決於您建構 Chat 應用程式的方式:
- 使用 Apps Script 建立可擴充 Chat 的 Google Workspace 外掛程式
- 獨立 Apps Script Chat 應用程式
- 透過 HTTP 擴充 Chat 的 Google Workspace 外掛程式
- 獨立 HTTP Chat 應用程式
Apps Script
如果您使用 Apps Script 建構 Chat 應用程式,Apps Script 會自動處理精細的 OAuth 權限。不過,請確保程式碼能處理使用者未授予所有要求範圍的情況。方法取決於您的 Apps Script 是使用 Apps Script 擴充 Google Chat 的 Google Workspace 外掛程式,還是使用 Apps Script 和互動事件建構的獨立 Chat 擴充應用程式。
擴充 Chat 功能的 Google Workspace 外掛程式
如果您是使用 Apps Script 擴充 Google Chat 的 Google Workspace 外掛程式,請按照「在 Apps Script 中處理精細 OAuth 權限」一文中的操作說明進行。
獨立的 Apps Script Chat 應用程式
如果您使用 Apps Script 和互動事件建構 Chat 應用程式,請注意,在 Apps Script 中處理精細的 OAuth 權限一文中的操作說明適用於以下情況:
ScriptApp.requireScopes
如果未授予指定範圍,系統會停止執行指令碼,但使用者會在 Chat 中看到設定資訊卡,而不是 OAuth 同意畫面。設定卡片一律會提示使用者授予所有要求的範圍,而非僅授予未授予的範圍。
如要提供個別授權範圍層級檢查,請使用 ScriptApp.getAuthorizationInfo 檢查授權,並視需要使用私人訊息要求授權。
以下範例說明如何檢查特定權限 (例如日曆存取權),如果缺少權限,則傳回含有必要授權網址的私人訊息。
Apps Script
/**
* Responds to a MESSAGE event in Google Chat.
* Checks for required permissions and if missing asks for them.
*
* @param {Object} event the event object from Chat
* @return {Object} JSON response
*/
function onMessage(event) {
// Check if the script has the necessary permissions.
// In this example, the script checks for the "calendar.events" scope.
var requiredScopes = ['https://www.googleapis.com/auth/calendar.events'];
var authInfo = ScriptApp.getAuthorizationInfo(ScriptApp.AuthMode.FULL, requiredScopes);
// If permissions are missing, return a message with the authorization URL.
if (authInfo.getAuthorizationStatus() === ScriptApp.AuthorizationStatus.REQUIRED) {
var authUrl = authInfo.getAuthorizationUrl();
return {
"text": "This action requires authorization. Please <" + authUrl + "|click here to authorize>.",
"privateMessageViewer": {
"name": event.user.name
}
};
}
// Permission granted; proceed with the application logic.
// ...
}
HTTP 端點
如果您使用 HTTP 端點建構 Chat 應用程式,Chat 應用程式應支援精細的 OAuth 權限。
擴充 Chat 功能的 Google Workspace 外掛程式
如果將 Chat 應用程式建構為 Google Workspace 外掛程式,請設定程式碼來處理精細的 OAuth 權限。檢查使用者授予的授權範圍,並視需要要求授權缺少或所有範圍。
在外掛程式的資訊清單檔案中,於
oauthScopes欄位指定必要的授權範圍。這個欄位是projects.deployments資源的一部分。以下範例需要
chat.messages和calendar.events授權範圍:JSON
{ "oauthScopes": [ "https://www.googleapis.com/auth/chat.messages", "https://www.googleapis.com/auth/calendar.events" ], "addOns": { "common": { "name": "My Chat App", "logoUrl": "https://lh3.googleusercontent.com/..." }, "chat": {}, "calendar": {}, "httpOptions": {} } }如要查看使用者授予的範圍,請檢查
authorizationEventObject.authorizedScopes欄位。如果缺少必要範圍,請傳回requesting_google_scopes動作,提示使用者提供缺少的範圍。Node.js
// Check for authorized scopes. const authorizedScopes = req.body.authorizationEventObject?.authorizedScopes || []; if (!authorizedScopes.includes('https://www.googleapis.com/auth/chat.messages')) { // Respond with a request for the missing scope. res.send({ 'requesting_google_scopes': { 'scopes': ['https://www.googleapis.com/auth/chat.messages'] } }); return; }Python
from flask import jsonify, request # Check for authorized scopes. event_data = request.get_json() authorized_scopes = event_data.get('authorizationEventObject', {}).get('authorizedScopes', []) if 'https://www.googleapis.com/auth/chat.messages' not in authorized_scopes: # Respond with a request for the missing scope. return jsonify({ 'requesting_google_scopes': { 'scopes': ['https://www.googleapis.com/auth/chat.messages'] } })Java
import com.google.gson.JsonArray; import com.google.gson.JsonObject; import java.util.List; // Check for authorized scopes. List<String> authorizedScopes = event.getAuthorizationEventObject() != null ? event.getAuthorizationEventObject().getAuthorizedScopes() : null; if (authorizedScopes == null || !authorizedScopes.contains("https://www.googleapis.com/auth/chat.messages")) { // Respond with a request for the missing scope. JsonObject requestingGoogleScopes = new JsonObject(); JsonArray scopes = new JsonArray(); scopes.add("https://www.googleapis.com/auth/chat.messages"); requestingGoogleScopes.add("scopes", scopes); JsonObject response = new JsonObject(); response.add("requesting_google_scopes", requestingGoogleScopes); return response.toString(); }如要要求與外掛程式相關聯的所有範圍,請將
all_scopes設為true:Node.js
res.send({ 'requesting_google_scopes': { 'all_scopes': true } });Python
from flask import jsonify return jsonify({ 'requesting_google_scopes': { 'all_scopes': True } })Java
import com.google.gson.JsonObject; JsonObject requestingGoogleScopes = new JsonObject(); requestingGoogleScopes.addProperty("all_scopes", true); JsonObject response = new JsonObject(); response.add("requesting_google_scopes", requestingGoogleScopes); return response.toString();
如需詳細的操作說明,請參閱「管理 HTTP Google Workspace 外掛程式的精細權限」。
獨立 HTTP Chat 應用程式
如果 Chat 應用程式是獨立的 HTTP 服務 (而非 Google Workspace 外掛程式),您必須自行管理 OAuth 2.0 流程。
擷取已儲存的權杖或交換授權碼時,請檢查已授予哪些範圍。如果缺少必要範圍,請提示使用者授權。
Node.js
// 1. List authorized scopes.
const fs = require('fs');
const tokens = JSON.parse(fs.readFileSync('token.json'));
const grantedScopes = tokens.scope.split(' ');
// 2. Detect missing scopes.
const requiredScopes = ['https://www.googleapis.com/auth/chat.messages'];
const missingScopes = requiredScopes.filter(scope => !grantedScopes.includes(scope));
if (missingScopes.length > 0) {
// 3. Request missing scopes.
const authUrl = oauth2Client.generateAuthUrl({
access_type: 'offline',
scope: missingScopes,
include_granted_scopes: true
});
res.redirect(authUrl);
}
// To request all scopes instead of just the missing ones:
const allScopesAuthUrl = oauth2Client.generateAuthUrl({
access_type: 'offline',
scope: requiredScopes,
include_granted_scopes: true
});
Python
from flask import redirect
from google.oauth2.credentials import Credentials
# 1. List authorized scopes.
credentials = Credentials.from_authorized_user_file('token.json')
granted_scopes = set(credentials.scopes)
# 2. Detect missing scopes.
required_scopes = {'https://www.googleapis.com/auth/chat.messages'}
missing_scopes = required_scopes - granted_scopes
if missing_scopes:
# 3. Request missing scopes.
flow.scope = list(missing_scopes)
auth_url, _ = flow.authorization_url(
access_type='offline',
include_granted_scopes=True
)
return redirect(auth_url)
# To request all scopes instead of just the missing ones:
flow.scope = list(required_scopes)
all_scopes_auth_url, _ = flow.authorization_url(
access_type='offline',
include_granted_scopes='true'
)
Java
import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeRequestUrl;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
// 1. List authorized scopes.
// The "user" string is the user ID for which to load credentials.
Credential credential = flow.loadCredential("user");
Collection<String> grantedScopes = credential.getScopes();
// 2. Detect missing scopes.
// The `requiredScopes` variable contains a list of the OAuth scopes
// that your app requires to function. Define this variable with the
// scopes needed by your application.
List<String> requiredScopes = Arrays.asList("https://www.googleapis.com/auth/chat.messages");
List<String> missingScopes = new ArrayList<>();
for (String scope : requiredScopes) {
if (!grantedScopes.contains(scope)) {
missingScopes.add(scope);
}
}
if (!missingScopes.isEmpty()) {
// 3. Request missing scopes.
GoogleAuthorizationCodeRequestUrl urlBuilder = new GoogleAuthorizationCodeRequestUrl(
clientId, redirectUri, missingScopes)
.setAccessType("offline")
.set("include_granted_scopes", "true");
String authUrl = urlBuilder.build();
response.sendRedirect(authUrl);
}
// To request all scopes instead of just the missing ones:
GoogleAuthorizationCodeRequestUrl allScopesUrlBuilder = new GoogleAuthorizationCodeRequestUrl(
clientId, redirectUri, requiredScopes)
.setAccessType("offline")
.set("include_granted_scopes", "true");
String allScopesAuthUrl = allScopesUrlBuilder.build();
詳情請參閱「精細 OAuth 權限」。
相關主題
- 如要瞭解 Google Chat 的驗證和授權機制,請參閱「瞭解驗證和授權」。
- 如要設定使用者驗證,請參閱「以 Google Chat 使用者身分進行驗證及授權」。
- 如需在 Apps Script 或 HTTP 適用的 Google Workspace 外掛程式中設定精細 OAuth 權限的說明,請參閱:
- 如要進一步瞭解精細 OAuth 權限,請參閱「精細 OAuth 權限」。