部署在Cloud Run的React应用无法使用google-auth-library调用API问题排查
问题分析与解决
问题描述
我将一个基础React应用打包部署为Cloud Run应用,希望通过服务分配的服务账号调用另一个Cloud Run API服务。使用Express Node的Cloud Run应用时,相同的google-auth-library实现可以正常工作,但React应用部署后调用失败——本地手动配置服务账号key.json时能正常运行。
部署后报错:
GET https://metadata.google.internal./computeMetadata/v1/instance net::ERR_NAME_NOT_RESOLVED TypeError: Unexpected error determining execution environment
核心原因
问题本质是运行环境不匹配:
google-auth-library在后端环境(如Express Cloud Run容器)中,会自动通过GCP元数据服务器获取服务账号凭证,这个元数据服务器metadata.google.internal是GCP内部私有地址,只能在GCP的后端容器内访问。- 但React代码是前端代码,最终会被打包发送到用户的浏览器中运行,浏览器属于客户端环境,根本无法访问GCP内部的元数据服务器,所以会出现域名解析失败的错误。
- 另外,前端直接处理服务账号凭证存在严重安全风险,一旦代码被反编译,服务账号密钥会泄露,导致GCP资源被滥用。
解决方案
方案1:添加后端中间层(推荐)
在React应用和目标Cloud Run API之间搭建一个后端服务(比如用Express部署在Cloud Run上),由这个中间层来处理服务账号认证逻辑:
- 后端服务使用
google-auth-library完成服务间认证,调用目标Cloud Run API。 - React应用只需要调用这个后端服务的公开接口,无需处理任何GCP认证逻辑。
示例后端代码(Express):
const express = require('express'); const {GoogleAuth} = require('google-auth-library'); const app = express(); app.get('/api/proxy', async (req, res) => { const url = "https://myapp.run.app/"; const targetAudience = "12345.apps.googleusercontent.com"; const auth = new GoogleAuth(); const client = await auth.getIdTokenClient(targetAudience); const apiRes = await client.request({url}); res.json(apiRes.data); }); const port = process.env.PORT || 8080; app.listen(port, () => { console.log(`Server running on port ${port}`); });
React代码修改为调用中间层:
class Overview extends Component { async componentDidMount(){ const res = await fetch('/api/proxy'); const data = await res.json(); console.info(data); } render(){ return ( <div> <h1>Overview</h1> </div> ) } } export default Overview;
方案2:改用用户身份认证
如果你的场景是面向终端用户的访问,可以让用户通过Google Sign-In或Firebase Auth登录,然后将用户的ID令牌发送给目标Cloud Run API,API验证令牌的合法性后提供服务。这种方式不需要服务账号,而是基于用户身份进行授权。
关键注意事项
- 永远不要在前端代码中嵌入服务账号密钥,也不要直接在前端使用
google-auth-library做服务间认证。 - Cloud Run的服务间认证是为后端服务之间的调用设计的,不适用于前端客户端。
内容的提问来源于stack exchange,提问作者Flamingow
相关产品推荐
相关产品推荐

