You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

通过Google Apps Script调用需ServiceAccount认证的Cloud Function遇401错误求助

排查Google Apps Script调用Cloud Function的401未授权问题

已完成的前置配置

  • 已为Google Apps Script添加官方最新版(v43)OAuth2库
  • Google Apps Script脚本与Cloud Function所属GCP项目关联
  • 同一GCP项目内创建了ServiceAccount
  • 该ServiceAccount已配置Owner、Logs Writer、Cloud Functions Admin、Cloud Functions Invoker角色
  • 已为ServiceAccount生成JSON密钥文件并复制到脚本中

问题现状

调用Cloud Function后始终返回:

401 Error: Unauthorized Your client does not have permission to the requested URL

已尝试的解决步骤

  • 允许未认证调用时可正常调用Cloud Function
  • 尝试使用OAuth2库的JWT方法,仍返回相同错误
  • 使用ChatGPT生成的类似代码,错误依旧

调用代码

const serviceAccount = {
  "type": "service_account",
  "project_id": "xxx",
  "private_key_id": "xxx",
  "private_key": "xxx",
  "client_email": "xxx",
  "client_id": "xxx",
  "auth_uri": "https://accounts.google.com/o/oauth2/auth",
  "token_uri": "https://oauth2.googleapis.com/token",
  "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
  "client_x509_cert_url": "xxx",
  "universe_domain": "googleapis.com"
};

const createService = (name, serviceAccount, scopes) => {
  return OAuth2.createService(name)
    .setTokenUrl('https://accounts.google.com/o/oauth2/token')
    .setPrivateKey(serviceAccount.private_key)
    .setIssuer(serviceAccount.client_email)
    .setPropertyStore(PropertiesService.getScriptProperties())
    .setCache(CacheService.getUserCache())
    .setLock(LockService.getUserLock())
    .setScope(scopes);
};

const sendRequest = (url, oauthParams) => {
  const { serviceName, serviceAccount, scopes } =
    oauthParams;

  const oauthService = createService(
    serviceName,
    serviceAccount,
    scopes
  );

  if (!oauthService.hasAccess()) {
    console.log(oauthService.getLastError());
    return;
  }

  const headers = {
    Authorization: `Bearer ${oauthService.getAccessToken()}`,
  };

  const options = {
    method: 'get',
    headers,
    muteHttpExceptions: true,
  };

  return UrlFetchApp.fetch(url, options).getContentText();
};

const callCF = () => {
  const url = 'xxx';
  const oauthParams = {
    serviceName: 'GWMservice',
    serviceAccount,
    scopes: ['https://www.googleapis.com/auth/cloud-platform'],
  };

  console.log(sendRequest(url, oauthParams));
};

callCF();

排查方向与修复方案

  1. 修正Token URL
    当前createService中使用的https://accounts.google.com/o/oauth2/token不适合ServiceAccount JWT认证,需替换为serviceAccount对象中自带的token_uri(即https://oauth2.googleapis.com/token),确保获取的是服务端专用令牌。

  2. 指定令牌受众(Audience)
    调用Cloud Function时,JWT令牌的受众必须匹配函数的URL。修改createService方法,添加.setAudience(url)绑定目标函数地址:

    const createService = (name, serviceAccount, scopes, url) => {
      return OAuth2.createService(name)
        .setTokenUrl(serviceAccount.token_uri)
        .setPrivateKey(serviceAccount.private_key)
        .setIssuer(serviceAccount.client_email)
        .setPropertyStore(PropertiesService.getScriptProperties())
        .setCache(CacheService.getUserCache())
        .setLock(LockService.getUserLock())
        .setScope(scopes)
        .setAudience(url); // 绑定函数URL作为受众
    };
    

    同时在sendRequest中传递url参数给createService。

  3. 检查ServiceAccount密钥完整性
    确认脚本中private_key字段完整包含-----BEGIN PRIVATE KEY-----和-----END PRIVATE KEY-----,且无换行符丢失或多余空格。

  4. 清除旧令牌缓存
    脚本通过CacheService存储令牌,可能存在过期或错误的旧令牌:

    • 打开Google Apps Script编辑器→项目设置→脚本属性,删除所有相关属性
    • 在代码中临时添加oauthService.reset()重置服务,清除缓存的旧令牌
  5. 验证Cloud Function的IAM绑定
    登录GCP控制台,进入目标Cloud Function的「权限」页面,确认ServiceAccount被直接授予roles/cloudfunctions.invoker权限,而非通过组或其他间接方式绑定。

内容的提问来源于stack exchange,提问作者JShinigami

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 12:34:55