从Cloud Functions访问已获Add-on权限的Google表格的认证问题咨询
根因定位
你之前尝试的编辑器服务账号、App Engine默认账号密钥认证方案从身份权限模型上就不成立,和代码写法、项目配置无关:
- 终端用户给Add-on的授权,主体是安装Add-on的用户本人身份,授权边界是Add-on通过
drive.file、spreadsheets.currentonly这类scope可接触到的特定Spreadsheet文件。 - 服务账号、App Engine默认账号是Google项目下的独立无头身份,从未获得过终端用户对这些Spreadsheet的访问授权,哪怕和Add-on同属一个项目,调用Sheets API时也会直接返回403权限不足。
- 硬靠扩大服务账号的权限范围绕开限制,又会突破当前Add-on已申请的scope边界,触发OAuth发布审核拦截,完全不适合面向普通用户的公开Add-on场景。
可落地方案(完全匹配现有已授权scope,无需新增权限)
方案1(推荐,轻量实现):用户身份令牌中转
核心逻辑是把用户给Add-on的授权身份直接传递给Cloud Function,全程不使用独立服务账号的凭证:
- 首次同步阶段,Add-on在拿到目标Spreadsheet ID、完成年度数据写入后,通过Apps Script内置的
ScriptApp.getIdentityToken()方法,生成一个绑定当前用户授权身份的OIDC访问令牌,将「令牌关联的用户ID、电商店铺ID、对应Spreadsheet ID」的映射关系存在Cloud Firestore中。 - 日常新销售触发时,电商平台通知Cloud Function拿到对应店铺ID的事件,先根据店铺ID从Firestore查到关联的用户身份映射,向Add-on所在的Apps Script项目发起请求,由运行在用户授权上下文的Add-on接口返回对应的有效身份令牌。
- Cloud Function拿到令牌后,直接使用该令牌构建Sheets API的认证客户端,执行新销售数据的写入操作——这个令牌天然携带你现有Add-on的全部Sheets、Drive访问权限,完全不会出现权限不足的问题。
Cloud Function侧的核心调用代码示例:
import { google } from 'googleapis'; const sheets = google.sheets('v4'); async function appendSalesData(spreadsheetId, accessToken, newSalesRecord) { // 直接用接收到的用户访问令牌构建认证客户端,无需引入本地service-account.json const authClient = new google.auth.OAuth2(); authClient.setCredentials({ access_token: accessToken }); // 执行元数据查询/数据写入操作 return sheets.spreadsheets.values.append({ auth: authClient, spreadsheetId: spreadsheetId, range: 'A1', valueInputOption: 'USER_ENTERED', requestBody: { values: newSalesRecord } }); }
方案2(低延迟场景适配):Apps Script Web App做权限代理
如果不想在每次新销售触发时临时拉取用户令牌,可以在Add-on首次授权完成后,部署一个仅授权访问的Apps Script Web App接口:
- 配置Web App的执行为「以用户身份访问」,将Web App的接口地址和对应店铺ID、Spreadsheet ID的映射存在数据库中。
- 当Cloud Function收到新销售通知时,根据映射关系调用这个Web App接口,由Web App在自带的用户授权上下文中完成Sheets写入,全程不需要在Cloud Function侧配置任何独立的认证凭证。
常见坑点排查
- 不要尝试为服务账号开启域级委托:这套机制仅适用于Workspace内部的私有工具,面向普通终端用户的公开Add-on场景下,普通用户根本不会给服务账号做域级权限授予,从流程上就不可行。
- 不要将
script.external_request的出站请求权限和身份权限混淆:这个scope仅允许Add-on发起外部网络请求,不会自动给关联的Cloud Function授予任何用户的文件访问权限。 - 你之前配置的service-account.json在这套流程中完全不需要使用,之前的权限不足问题和密钥配置无关,本质是用错了认证主体。
内容的提问来源于stack exchange,提问作者Leandro Zubrezki
相关产品推荐
相关产品推荐

