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

Apps Script至AppEngine后端服务的REST API授权实现问询

解决Apps Script到App Engine后端的授权问题

我刚好处理过类似的场景,官方那篇OAuth文档确实偏向于用户授权第三方服务,不太适配你的插件→后端的内部调用需求。下面是我验证过的可行方案,核心是利用Google ID Token来做双向身份验证:

核心思路

让你的Apps Script插件获取用户的ID Token(绑定到你的后端服务),后端收到请求后验证这个Token的有效性,确保请求来自合法的插件用户,同时可以关联用户身份做权限控制。

步骤一:前置配置

  1. 统一Cloud项目:尽量让Apps Script项目和App Engine后端归属同一个Google Cloud项目,这样权限配置会更顺畅;如果是G Suite域内使用,确保两者都在域的管理范围内。
  2. 创建后端OAuth客户端ID:
    • 进入Cloud Console的「API和服务→凭据」页面
    • 创建「OAuth客户端ID」,类型选「Web应用」
    • 记录生成的客户端ID,后续验证会用到
  3. 配置Apps Script OAuth范围:
    • 在你的表格插件项目里,打开「项目设置→显示"appsscript.json"清单文件」
    • 在oauthScopes数组中添加以下两个范围:
      "oauthScopes": [
        "https://www.googleapis.com/auth/userinfo.email",
        "https://www.googleapis.com/auth/script.external_request"
      ]
      
    • 第一个范围用于获取用户身份信息,第二个用于发起外部HTTP请求

步骤二:Apps Script端代码实现

在插件中调用后端时,先获取绑定到后端URL的ID Token,然后通过Authorization头传递:

function callBackendAPI() {
  const backendEndpoint = "https://your-appengine-project.appspot.com/api/your-endpoint";
  // 获取ID Token,参数是后端的URL,确保Token的受众(aud)匹配后端服务
  const idToken = ScriptApp.getIdToken(backendEndpoint);
  
  const requestOptions = {
    method: "POST", // 根据你的接口调整方法
    headers: {
      "Authorization": `Bearer ${idToken}`,
      "Content-Type": "application/json"
    },
    payload: JSON.stringify({ /* 你的请求数据 */ })
  };
  
  try {
    const response = UrlFetchApp.fetch(backendEndpoint, requestOptions);
    return JSON.parse(response.getContentText());
  } catch (error) {
    console.error("调用后端失败:", error);
    throw error;
  }
}

步骤三:App Engine后端验证逻辑

以Python Flask为例,使用Google官方的google-auth库验证ID Token的有效性:

from google.oauth2 import id_token
from google.auth.transport import requests
from flask import Flask, request, jsonify

app = Flask(__name__)
# 替换成你之前创建的后端OAuth客户端ID
EXPECTED_AUDIENCE = "your-backend-oauth-client-id.apps.googleusercontent.com"
# 如果是G Suite域内使用,可指定允许的域名
ALLOWED_DOMAIN = "your-g-suite-domain.com"

def verify_request_token():
    auth_header = request.headers.get("Authorization")
    if not auth_header or not auth_header.startswith("Bearer "):
        return None, "缺少有效授权头"
    
    token = auth_header.split(" ")[1]
    try:
        # 验证Token的签名、有效期、受众等信息
        id_info = id_token.verify_oauth2_token(token, requests.Request(), EXPECTED_AUDIENCE)
        # 验证用户是否属于指定G Suite域(可选)
        if id_info.get("hd") != ALLOWED_DOMAIN:
            return None, "用户不属于授权域"
        return id_info, None
    except ValueError as e:
        return None, f"Token验证失败:{str(e)}"

@app.route("/api/your-endpoint", methods=["POST"])
def handle_request():
    user_info, error = verify_request_token()
    if error:
        return jsonify({"error": error}), 401
    
    # 验证通过,处理业务逻辑
    return jsonify({
        "message": "请求成功",
        "user_email": user_info["email"],
        "user_id": user_info["sub"]
    })

if __name__ == "__main__":
    app.run()

额外注意事项

  • OAuth同意屏幕配置:如果是域内使用,把同意屏幕设置为「内部」;如果对外发布,需要完成验证流程,避免用户看到不安全的提示。
  • Token有效期:ID Token的有效期是1小时,Apps Script会自动刷新,无需手动处理。
  • 权限控制:后端可以通过user_info中的邮箱或用户ID,结合G Suite域的权限体系,实现更精细的访问控制。

这个方案完全依托Google的身份生态,不需要自己搭建复杂的授权系统,安全性和可维护性都很高,刚好适配你的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 03:35:54