使用google-auth-library调用Cloud Function时出现401 Unauthorized问题
本地使用个人身份认证或模拟指定服务账号调用私有HTTP Cloud Function均正常,但将代码部署到Cloud Run(使用同一服务账号)后,所有调用均返回401未授权响应。Cloud Run实例通过Serverless VPC Access连接器处理所有出站流量,尝试过相关解决方案但无效。
代码基本结构:
import { GoogleAuth } from 'google-auth-library'; async function callCloudFunction() { const functionUrl = 'https://REGION-PROJECT_ID.cloudfunctions.net/FUNCTION_NAME'; const auth = new GoogleAuth(); const client = await auth.getIdTokenClient(functionUrl); const token = await client.getRequestHeaders(); const res = await fetch(functionUrl, { method: 'GET', headers: token }); const data = await res.json(); return data }
可能的问题及排查方向
VPC环境下的域名解析与调用地址问题:
私有Cloud Function通过VPC访问时,应使用内部调用地址而非公网域名。公网域名https://REGION-PROJECT_ID.cloudfunctions.net/FUNCTION_NAME在VPC内可能被解析到公网IP,导致认证逻辑不匹配。改用内部地址格式:http://FUNCTION_NAME-PROJECT_ID.REGION.run.app(注意使用HTTP协议,内部流量默认不加密)。身份令牌受众不匹配:
getIdTokenClient生成的令牌受众默认是传入的URL,若实际调用的是内部地址但令牌受众仍为公网域名,会导致Cloud Function验证令牌失败。需确保传入getIdTokenClient的受众参数与实际调用的内部地址一致。Metadata服务访问受限:
google-auth-library依赖GCP Metadata服务获取身份令牌,若VPC连接器的防火墙规则阻止了对169.254.169.254(Metadata服务地址)的出站HTTP流量,会导致无法获取有效令牌。检查VPC防火墙规则,允许Cloud Run实例所在子网向该地址发起HTTP请求。Cloud Function的私有访问配置:
确认私有Cloud Function已配置为允许来自Cloud Run所在VPC的访问。在Cloud Function的设置中,检查“VPC网络”配置,确保允许目标VPC的内部流量访问,或确认Cloud Run服务账号已拥有cloudfunctions.invoker权限。VPC连接器的网络配置:
检查Serverless VPC Access连接器是否与Cloud Function所在VPC网络正确关联,且连接器的子网范围未被防火墙规则限制。确保Cloud Run实例通过连接器能正常访问Cloud Function所在的VPC资源。
修正后的代码示例
import { GoogleAuth } from 'google-auth-library'; async function callCloudFunction() { // 使用Cloud Function内部调用地址 const functionUrl = 'http://FUNCTION_NAME-PROJECT_ID.REGION.run.app'; const auth = new GoogleAuth(); // 指定内部地址为令牌受众,确保匹配 const client = await auth.getIdTokenClient(functionUrl); const token = await client.getRequestHeaders(); const res = await fetch(functionUrl, { method: 'GET', headers: token }); // 增加错误日志便于排查 if (!res.ok) { const errorText = await res.text(); console.error(`Cloud Function调用失败:状态码${res.status},响应内容:${errorText}`); throw new Error(`调用失败:${res.status}`); } const data = await res.json(); return data; }
内容的提问来源于stack exchange,提问作者Matt Kocak

