使用用户身份验证的聊天应用必须支持精细的 OAuth 权限,以允许用户授予所请求范围的子集。例如,用户可能会授予对其名称的访问权限,但拒绝授予对其日历的访问权限。
处理精细 OAuth 权限取决于您构建 Chat 应用的方式:
- 扩展 Chat 的 Apps 脚本 Google Workspace 插件
- 独立式 Apps 脚本 Chat 应用
- 扩展 Chat 的 HTTP Google Workspace 加购项
- 独立 HTTP Chat 应用
Apps 脚本
如果您使用 Apps 脚本构建 Chat 应用,Apps 脚本会自动处理细化 OAuth 权限。不过,请确保您的代码能够处理用户未授予所有请求范围的情况。具体方法取决于您的 Apps 脚本是使用 Apps 脚本扩展 Google Chat 的 Google Workspace 插件,还是使用 Apps 脚本和互动事件构建的独立 Chat 应用。
可扩展 Chat 的 Google Workspace 插件
如果您将 Chat 扩展应用构建为使用 Apps 脚本扩展 Google Chat 的 Google Workspace 插件,请按照在 Apps 脚本中处理 OAuth 精细权限中的说明进行操作。
独立 Apps 脚本 Chat 应用
如果您使用 Apps 脚本和互动事件构建 Chat 应用,则 在 Apps 脚本中处理精细的 OAuth 权限中的说明适用,但需要考虑以下事项:
ScriptApp.requireScopes 如果未授予指定范围,则停止脚本执行,但用户会在 Chat 中看到配置卡片,而不是 OAuth 权限请求页面。配置卡片始终会提示用户授予所有请求的范围,而不仅仅是未授予的范围。
如需提供各个授权范围级别的检查,请使用 ScriptApp.getAuthorizationInfo 检查授权情况,并在必要时使用私信请求授权。
以下示例展示了如何检查特定权限(例如日历访问权限),如果缺少该权限,则返回包含所需授权网址的私信。
Apps 脚本
/**
* 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 应用,则该应用应支持精细的 OAuth 权限。
可扩展 Chat 的 Google Workspace 插件
如果您将 Chat 应用构建为 Google Workspace 插件(例如,如果该应用扩展了其他 Google Workspace 应用,如 Google 云端硬盘或 Gmail),请配置清单文件和代码以处理精细的 OAuth 权限:
在插件的清单文件中,将
granularOauthPermissionSupport字段设置为OPT_IN。如需详细了解granularOauthPermissionSupport字段,请参阅迁移到精细的 OAuth 权限流程。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": {}, "httpOptions": { "granularOauthPermissionSupport": "OPT_IN" } } }如需查看用户授予了哪些范围,请在代码中检查
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().getAuthorizedScopes(); if (!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 脚本或 HTTP Google Workspace 插件中设置精细 OAuth 权限方面的帮助,请参阅:
- 如需详细了解细化的 OAuth 权限,请参阅细化的 OAuth 权限。