如何通过Firebase Functions结合Gmail API从Google Workspace发邮件?
如何通过Firebase Cloud Function结合Google Workspace账号发送邮件?
背景需求
我需要创建一个名为sendEmail的Firebase云函数,用于通过Google Workspace邮箱发送邮件,React前端通过以下代码调用:
const sendEmailFn = httpsCallable(functions, 'sendEmail'); const response = await sendEmailFn({ email, subject, message });
最初实现与报错
初始云函数代码
const functions = require("firebase-functions"); const {google} = require("googleapis"); const oAuth2Client = new google.auth.OAuth2( process.env.CLIENT_ID, process.env.CLIENT_SECRET, process.env.CALLBACK_URL, ); const gmailClient = new google.gmail({ version: "v1", auth: oAuth2Client, }); exports.sendEmail = functions.https.onCall(async (data, context) => { const {email, subject, message} = data; const emailContent = ` From: "Sender Name" <email@example.com> To: "Recipient Name" <${email}> Subject: ${subject} Content-Type: text/html; charset=utf-8 ${message} `; try { await gmailClient.users.messages.send({ userId: "me", requestBody: { raw: Buffer.from(emailContent).toString("base64"), }, }); return {success: true}; } catch (error) { console.error("Error sending email:", error); throw new functions.https.HttpsError( "internal", "An error occurred while sending the email.", ); } });
遇到的错误
- 调用时日志报错:
I still get Error sending email: Error: No access, refresh token, API key or refresh handler callback is set.
- 尝试将API Key设为access_token后:
message: 'Invalid Credentials'
- 直接在
gmail.auth中设置API Key,请求返回401 Response,且Gmail API仅支持OAuth2客户端ID和服务账号,无法用普通API Key认证。
尝试服务账号后的新问题
改用服务账号认证后,代码如下:
// Create an OAuth2 client using the service account credentials const authClient = new google.auth.JWT({ email: keyFile.client_email, key: keyFile.private_key, scopes: ["https://www.googleapis.com/auth/gmail.send", "https://www.googleapis.com/auth/gmail.readonly"], }); authClient.authorize((err, tokens) => { if (err) { console.error("Error authenticating:", err); throw new Error("Failed to authenticate"); } else { console.log("Authentication successful"); } });
认证成功,但发送邮件时出现:
Code: 400, message: 'Precondition check failed.'
修改userId: "me"参数也无法解决问题。
正确解决方案
1. 完成服务账号与域范围委派配置
- 在Google云控制台创建服务账号,下载JSON格式的密钥文件
- 以Google Workspace管理员身份,在Admin控制台为该服务账号启用域范围委派,添加授权范围:
https://www.googleapis.com/auth/gmail.send - 这一步是核心:没有域范围委派,服务账号无法模拟Workspace用户发送邮件
2. 修正后的云函数代码
const functions = require("firebase-functions"); const { google } = require("googleapis"); // 建议用Firebase环境变量存储密钥,避免硬编码 const serviceAccount = JSON.parse(functions.config().service_account.key); const senderEmail = "your-workspace-email@example.com"; // 指定发件人邮箱 exports.sendEmail = functions.https.onCall(async (data, context) => { const { email, subject, message, recipientName = "Recipient" } = data; // 初始化JWT客户端,指定要模拟的Workspace用户 const authClient = new google.auth.JWT({ email: serviceAccount.client_email, key: serviceAccount.private_key, scopes: ["https://www.googleapis.com/auth/gmail.send"], subject: senderEmail, // 关键:指定要模拟的发件人邮箱 }); const gmailClient = google.gmail({ version: "v1", auth: authClient }); // 构造符合RFC 2822标准的邮件内容 const emailContent = [ `From: "Sender Name" <${senderEmail}>`, `To: "${recipientName}" <${email}>`, `Subject: ${subject}`, "Content-Type: text/html; charset=utf-8", "", // 空行分隔邮件头部与正文 message, ].join("\n"); // 转换为Gmail API要求的URL安全Base64编码 const raw = Buffer.from(emailContent) .toString("base64") .replace(/\+/g, "-") .replace(/\//g, "_") .replace(/=+$/, ""); try { await gmailClient.users.messages.send({ userId: senderEmail, // 用具体发件人邮箱替代"me" requestBody: { raw }, }); return { success: true }; } catch (error) { console.error("邮件发送失败:", error); throw new functions.https.HttpsError( "internal", "邮件发送过程中出现错误。" ); } });
3. 关键注意事项
- 域范围委派:必须由Workspace管理员操作,否则服务账号无法获得模拟用户的权限
- 邮件格式:严格遵循RFC 2822规范,头部与正文之间必须有空行
- Base64编码:Gmail API要求
raw字段为URL安全的Base64,需替换+、/、=字符 - userId参数:不能使用
"me",必须指定具体的发件人邮箱(与subject参数一致) - 密钥安全:不要将服务账号密钥硬编码到代码中,使用Firebase环境变量存储:
firebase functions:config:set service_account.key="$(cat service-account-key.json)"
错误原因总结
- 初始OAuth2客户端错误:缺少有效的access/refresh token,普通OAuth2流程需要用户授权,不适合无交互的云函数场景
- API Key无效:Gmail发送邮件接口不支持API Key认证,仅支持OAuth2或服务账号
- 服务账号400错误:未启用域范围委派,或未指定要模拟的Workspace用户(
subject参数缺失),导致无法代表用户发送邮件
内容的提问来源于stack exchange,提问作者kaiserm99
相关产品推荐
相关产品推荐

