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

使用Google Apps Script OAuth2令牌调用Cloud Function授权失败求助

Apps Script调用Cloud Function报401:IdentityToken无效的解决办法

问题核心原因

你用ScriptApp.getIdentityToken()获取的ID令牌受众(aud字段)不匹配Cloud Function的要求:

  • Cloud Function要求ID令牌的aud必须是函数的触发URL,或者对应GCP项目的客户端ID
  • 而Apps Script默认生成的IdentityToken,aud是脚本自身的项目ID,因此即使令牌有效,也无法通过Cloud Function的鉴权

可行解决方案

方案1:用OAuth2库生成带正确受众的ID令牌

通过官方OAuth2库指定target_audience参数,生成符合Cloud Function要求的ID令牌:

  1. 安装OAuth2库:在Apps Script编辑器中,依次点击「资源」→「库」,输入库ID 1B7FSrk5Zi6L1rSxxTDgDEUsPzlukDsi4KGuTMorsTQHhGBzBkMun4iDF,选择最新版本并保存。

  2. 配置OAuth服务:

function getValidIdToken() {
  const cloudFunctionUrl = "https://[REGION]-[PROJECT_ID].cloudfunctions.net/[YOUR_FUNCTION_NAME]";
  const gcpClientId = "[YOUR_GCP_PROJECT_CLIENT_ID]"; // 从GCP控制台「API和服务」→「凭据」获取
  const gcpClientSecret = "[YOUR_GCP_PROJECT_CLIENT_SECRET]"; // 同上

  return OAuth2.createService('CloudFunctionAuth')
    .setAuthorizationBaseUrl('https://accounts.google.com/o/oauth2/auth')
    .setTokenUrl('https://oauth2.googleapis.com/token')
    .setClientId(gcpClientId)
    .setClientSecret(gcpClientSecret)
    .setCallbackFunction('authCallback')
    .setScope('openid email')
    .setParam('target_audience', cloudFunctionUrl) // 关键:指定Cloud Function为受众
    .setParam('access_type', 'offline')
    .setParam('prompt', 'consent')
    .getIdToken(); // 获取ID令牌而非access token
}

// OAuth授权回调函数
function authCallback(request) {
  const service = OAuth2.createService('CloudFunctionAuth');
  const authorized = service.handleCallback(request);
  return HtmlService.createHtmlOutput(authorized ? '授权成功,可关闭此页面' : '授权失败');
}
  1. 调用Cloud Function:
function callCloudFunction() {
  const requestData = { key: "value" }; // 你的请求数据
  const cloudFunctionUrl = "https://[REGION]-[PROJECT_ID].cloudfunctions.net/[YOUR_FUNCTION_NAME]";
  const validIdToken = getValidIdToken();

  const options = {
    method: "post",
    contentType: "application/json",
    payload: JSON.stringify(requestData),
    headers: {
      'Authorization': `Bearer ${validIdToken}`
    }
  };

  const response = UrlFetchApp.fetch(cloudFunctionUrl, options);
  Logger.log(response.getContentText());
}

方案2:服务账号鉴权(适合无人值守场景)

如果脚本不需要用户交互,可使用GCP服务账号生成令牌:

  1. 创建服务账号:在GCP控制台「IAM与管理员」→「服务账号」,创建账号并授予「Cloud Functions Invoker」角色,下载JSON密钥文件。

  2. 用服务账号生成ID令牌:

function getServiceAccountToken() {
  const cloudFunctionUrl = "https://[REGION]-[PROJECT_ID].cloudfunctions.net/[YOUR_FUNCTION_NAME]";
  // 替换为你的服务账号密钥内容(或通过UrlFetchApp读取上传的密钥文件)
  const serviceAccountKey = {
    "type": "service_account",
    "project_id": "[PROJECT_ID]",
    "private_key_id": "[PRIVATE_KEY_ID]",
    "private_key": "[PRIVATE_KEY]",
    "client_email": "[CLIENT_EMAIL]",
    "client_id": "[CLIENT_ID]",
    ...
  };

  return OAuth2.createService('ServiceAccountAuth')
    .setAuthorizationBaseUrl('https://accounts.google.com/o/oauth2/auth')
    .setTokenUrl('https://oauth2.googleapis.com/token')
    .setPrivateKey(serviceAccountKey.private_key)
    .setIssuer(serviceAccountKey.client_email)
    .setSubject(serviceAccountKey.client_email)
    .setParam('target_audience', cloudFunctionUrl)
    .setScope('openid')
    .getIdToken();
}

验证步骤

生成令牌后,可通过https://oauth2.googleapis.com/tokeninfo?id_token=[TOKEN]解码,确认aud字段等于你的Cloud Function触发URL。同时确保:

  • Cloud Function的IAM权限中,当前用户/服务账号已拥有roles/cloudfunctions.invoker角色
  • 令牌未过期(exp字段为过期时间戳)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 00:12:23