Nodemailer搭配OAuth2发邮件报No refresh token或refresh handler未设置错误
你遇到的报错核心原因是调用oAuth2Client.getAccessToken()时,OAuth2客户端没有识别到有效的refresh_token,可按照以下步骤排查修复:
第一步:排查环境变量加载问题
先在oAuth2Client.setCredentials代码行前加一行打印console.log('refresh token: ', process.env.REFRESH_TOKEN),运行代码看输出是否为undefined。- 本地环境如果使用dotenv管理环境变量,需要确保项目入口文件最顶部已经引入并初始化dotenv:
require('dotenv').config(),且.env文件放在项目根目录,包含REFRESH_TOKEN等所有配置项。 - 部署到Heroku时需要在控制台的设置页,手动配置所有环境变量,不要将.env文件提交到代码仓库。
- 本地环境如果使用dotenv管理环境变量,需要确保项目入口文件最顶部已经引入并初始化dotenv:
第二步:修正代码冗余和错误配置
你当前的代码有两处多余配置:一是OAuth2认证不需要传pass参数,该参数是普通账号密码登录时使用的;二是创建transport不需要套smtTransport,直接给nodemailer传配置即可。修正后的核心代码参考:
const oAuth2Client = new google.auth.OAuth2( process.env.CLIENT_ID, process.env.CLIENT_SECRET, process.env.REDIRECT_URI ); oAuth2Client.setCredentials({ refresh_token: process.env.REFRESH_TOKEN }); // transport可以全局创建,不需要每次发邮件都新建 const transport = nodemailer.createTransport({ service: "gmail", auth: { type: "OAuth2", user: "kediaarts@gmail.com", clientId: process.env.CLIENT_ID, clientSecret: process.env.CLIENT_SECRET, refreshToken: process.env.REFRESH_TOKEN, } }); const sendMail = async (email, code) => { try { const accessToken = await oAuth2Client.getAccessToken(); // 动态给transport设置最新的accessToken transport.set('oauth2.accessToken', accessToken); const link = `http://localhost:5000/${email}/${code.toString()}`; const mailOptions = { from: '"Horizons" <no-reply@gmail.com>', to: email, subject: "Please verify your Email", html: `<h1>Horizons</h1><br><h2>Hi,<br> Please click on the link to verify your email.</h2><br><a href="${link}">Click here to verify</a>`, } const result = await transport.sendMail(mailOptions); return result; } catch(error) { console.log(error); } }
- 第三步:验证refresh_token有效性
如果环境变量正常加载还是报错,说明你的refresh_token已经失效:- Google Cloud控制台中如果你的应用处于「测试」状态,授权生成的refresh_token有效期只有7天,过期后需要重新走授权流程生成新的refresh_token。
- 确认授权时已经勾选了Gmail发送权限,对应的scope为
https://www.googleapis.com/auth/gmail.send,没有正确授权的refresh_token无法调用发信接口。
内容的提问来源于stack exchange,提问作者Naman Kedia
相关产品推荐
相关产品推荐

