使用Nodemailer与Google API发送邮件时invalid_grant错误的原因排查及解决方法
解决Nodemailer + Google OAuth2的
invalid_grant错误 那个invalid_grant错误在Google OAuth2集成里真的是高频坑,我结合你的配置和代码,给你梳理几个最可能的原因和解决办法:
1. 最容易忽略的:环境变量名不匹配
看你的console.log输出:
console.log("CLIENT_REDIRECT_URI: " + process.env.REDIRECT_URI);
但你提到.env里的键是CLIENT_REDIRECT_URI,这就导致代码里读取的重定向URI是空的或者错误的!Google OAuth2对重定向URI的匹配要求极其严格,哪怕差一个字符都会直接返回invalid_grant。
解决办法:
要么把.env里的CLIENT_REDIRECT_URI改成REDIRECT_URI,要么把代码里的process.env.REDIRECT_URI改成process.env.CLIENT_REDIRECT_URI,保证和你在Google Console里配置的重定向URI完全一致(包括http/https、端口号、结尾的斜杠)。
2. Refresh Token失效或无效
Google的Refresh Token不是永久有效的,尤其是在测试模式下:
- 如果你的OAuth应用还处于「测试状态」(未提交生产审核),Refresh Token会在7天后过期;
- 如果你重新授权了同一账号,旧的Refresh Token会被作废;
- 授权时用的账号不在测试用户列表里(测试模式下只有列表内的账号能授权)。
解决办法:
- 去Google Cloud Console的「OAuth同意屏幕」页面,检查应用状态。如果是测试模式,要么把应用发布为生产(需要Google审核),要么重新走一次授权流程获取新的Refresh Token;
- 确保授权时使用的账号在测试用户列表中。
3. OAuth客户端配置或API未启用
- 你可能选错了OAuth客户端类型:本地调试应该选「桌面应用」,Web应用的重定向URI规则不同;
- 忘记启用Gmail API:没有启用API的话,哪怕配置正确也无法调用。
解决办法:
- 打开Google Cloud Console的「API与服务」→「库」,搜索并启用Gmail API;
- 检查「凭据」页面的OAuth客户端类型,本地开发建议选「桌面应用」,对应的重定向URI用
http://localhost或http://localhost:xxxx(要和授权时用的一致)。
4. 代码里的小细节优化
你的代码里还有几个可以统一的地方,避免潜在问题:
mailOptions里的from字段硬编码了邮箱,建议改成和user字段一致的process.env.OWNER_EMAIL,确保是授权时用的同一个账号;getAccessToken()返回的accessToken.token可以直接用oAuth2Client.credentials.access_token,简化代码。
修正后的代码示例
const nodemailer = require('nodemailer'); const { google } = require('googleapis'); require('dotenv').config(); // 修正环境变量名匹配问题 console.log("CLIENT_ID: " + process.env.CLIENT_ID); console.log("CLIENT_SECRET: " + process.env.CLIENT_SECRET); console.log("CLIENT_REDIRECT_URI: " + process.env.CLIENT_REDIRECT_URI); console.log("REFRESH_TOKEN: " + process.env.REFRESH_TOKEN); const oAuth2Client = new google.auth.OAuth2( process.env.CLIENT_ID, process.env.CLIENT_SECRET, process.env.CLIENT_REDIRECT_URI // 这里修正为正确的环境变量 ); console.log("oAuth2Client: " + oAuth2Client); oAuth2Client.setCredentials({refresh_token: process.env.REFRESH_TOKEN}) async function sendMail() { try { await oAuth2Client.getAccessToken(); // 提前刷新token const transport = nodemailer.createTransport({ host: "smtp.gmail.com", port: 465, secure: true, auth: { type: 'OAuth2' } }); const mailOptions = { from: process.env.OWNER_EMAIL, // 统一用环境变量 to: process.env.OWNER_EMAIL, subject: "Test Email API Subject", text: "Test Email API Text", html: "<h1> Test Email API HTML </h1>", auth: { user: process.env.OWNER_EMAIL, accessToken: oAuth2Client.credentials.access_token // 直接用已刷新的token } } const result = await transport.sendMail(mailOptions); console.log('邮件发送成功:', result); return result; } catch (error) { console.log(error.stack); return error; } } sendMail()
建议你先优先排查环境变量名的问题,这个是最快速能解决的,然后再依次检查Refresh Token和OAuth配置。
内容的提问来源于stack exchange,提问作者ENV
相关产品推荐
相关产品推荐

