求助:Google Apps Script中Firebase服务账户生成的认证令牌无效
解决Google Apps Script中用服务账号生成Firebase客户端认证令牌的问题
我完全懂你现在的困境——用服务账号生成的令牌在客户端验证时总是弹出INVALID_TOKEN: Failed to validate MAC错误,只有已经废弃的database secret能正常工作。这其实是因为FirebaseApp/GSApp这类第三方库的createAuthToken方法,在传入OAuth access token时并没有按照Firebase自定义令牌的要求正确签名,而是沿用了database secret的HMAC签名逻辑,才导致客户端验证失败。
下面是直接可行的解决方案:手动生成符合Firebase规范的JWT自定义令牌,用服务账号的私钥进行RS256签名,彻底摆脱对第三方库的依赖。
具体步骤与代码实现
1. 替换原有的makeToken函数
删掉你原来的makeToken,改用下面的代码直接生成合规的Firebase自定义令牌:
function makeToken() { const serviceEmail = fb_SERVICE_EMAIL; // 你的服务账号邮箱 const privateKey = fb_PRIVATE_KEY.replace(/\\n/g, '\n'); // 注意处理私钥里的转义换行符 const userEmail = Session.getActiveUser().getEmail(); const uid = userEmail; // 可以用用户邮箱作为唯一标识,也可以自定义其他uid // 1. 构造JWT头部,固定RS256签名算法 const header = JSON.stringify({ alg: 'RS256', typ: 'JWT' }); // 2. 构造JWT负载,必须包含Firebase要求的字段 const now = Math.floor(Date.now() / 1000); const payload = JSON.stringify({ iss: serviceEmail, sub: serviceEmail, aud: 'https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit', iat: now, exp: now + 3600, // 过期时间最长1小时,不能超过这个值 uid: uid, email: userEmail, email_verified: true // 根据你的业务需求设置,这里默认用户已验证邮箱 }); // 3. 对头部和负载进行Base64URL编码 const encodedHeader = Utilities.base64EncodeWebSafe(header); const encodedPayload = Utilities.base64EncodeWebSafe(payload); // 4. 用服务账号私钥进行RS256签名 const signatureInput = `${encodedHeader}.${encodedPayload}`; const signatureBytes = Utilities.computeRsaSha256Signature(signatureInput, privateKey); const encodedSignature = Utilities.base64EncodeWebSafe(signatureBytes); // 5. 拼接成最终的认证令牌 return `${encodedHeader}.${encodedPayload}.${encodedSignature}`; }
2. 保留原有的客户端认证代码
你的客户端代码不需要做任何修改,直接使用这个新生成的令牌即可:
var userAuthToken; google.script.run.withSuccessHandler(function (requestAuthToken) { userAuthToken = authenticateClient(requestAuthToken) }).makeToken(); function authenticateClient(userRequestToken) { var ref = new Firebase(fb_URL); ref.authWithCustomToken(userRequestToken, function (error, authData) { if (error) { console.log("FB Login Failed!", error); } else { console.log("FB Login Succeeded!", authData); } }); return ref.authData?.auth; // 加个可选链避免未登录时的报错 }
为什么原来的方法不行?
Firebase的自定义令牌要求必须用服务账号的私钥进行RS256非对称签名,而database secret用的是HMAC对称签名。第三方库的createAuthToken方法在传入OAuth access token时,并没有切换到RS256签名模式,还是沿用了HMAC的签名逻辑,导致生成的令牌签名不符合Firebase的验证规则,所以才会出现MAC验证失败的错误。
手动生成JWT的方式严格遵循了Firebase的自定义令牌规范,所以能被客户端正确验证通过。
内容的提问来源于stack exchange,提问作者Chris
相关产品推荐
相关产品推荐

