Apps Script至AppEngine后端服务的REST API授权实现问询
解决Apps Script到App Engine后端的授权问题
我刚好处理过类似的场景,官方那篇OAuth文档确实偏向于用户授权第三方服务,不太适配你的插件→后端的内部调用需求。下面是我验证过的可行方案,核心是利用Google ID Token来做双向身份验证:
核心思路
让你的Apps Script插件获取用户的ID Token(绑定到你的后端服务),后端收到请求后验证这个Token的有效性,确保请求来自合法的插件用户,同时可以关联用户身份做权限控制。
步骤一:前置配置
- 统一Cloud项目:尽量让Apps Script项目和App Engine后端归属同一个Google Cloud项目,这样权限配置会更顺畅;如果是G Suite域内使用,确保两者都在域的管理范围内。
- 创建后端OAuth客户端ID:
- 进入Cloud Console的「API和服务→凭据」页面
- 创建「OAuth客户端ID」,类型选「Web应用」
- 记录生成的客户端ID,后续验证会用到
- 配置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
相关产品推荐
相关产品推荐

