Nodemailer发送邮件报535用户名密码不接受错误咨询
Nodemailer 对接 Mailtrap 报 EAUTH 535 认证失败排查
问题场景
- 技术栈:使用 Nodemailer 实现邮件发送能力,指定 Mailtrap.io 作为 SMTP 服务器
- 项目文件:
email.js:邮件公共配置、发送方法封装authController.js:包含forgotPassword忘记密码接口的处理逻辑
- 触发场景:请求接口
http://localhost:8000/api/v1/users/forgotPassword时返回错误 - 错误特征:错误码
EAUTH,SMTP响应码535,提示Username and Password not accepted,错误堆栈指向AUTH PLAIN认证流程失败
完整错误日志
Error: Invalid login: 535-5.7.8 Username and Password not accepted. Learn more at 535 5.7.8 BadCredentials g16-20020a05600c4ed000b003974860e15esm21842702wmq.40 - gsmtp at SMTPConnection._formatError (E:\starter\node_modules\nodemailer\lib\smtp-connection\index.js:784:19) at SMTPConnection._actionAUTHComplete (E:\starter\node_modules\nodemailer\lib\smtp-connection\index.js:1536:34) at SMTPConnection.<anonymous> (E:\starter\node_modules\nodemailer\lib\smtp-connection\index.js:540:26) at SMTPConnection._processResponse (E:\starter\node_modules\nodemailer\lib\smtp-connection\index.js:947:20) at SMTPConnection._onData (E:\starter\node_modules\nodemailer\lib\smtp-connection\index.js:749:14) at TLSSocket.SMTPConnection._onSocketData (E:\starter\node_modules\nodemailer\lib\smtp-connection\index.js:189:44) at TLSSocket.emit (node:events:527:28) at addChunk (node:internal/streams/readable:315:12) at readableAddChunk (node:internal/streams/readable:289:9) at TLSSocket.Readable.push (node:internal/streams/readable:228:10) at TLSWrap.onStreamRead (node:internal/stream_base_commons:190:23) { code: 'EAUTH', response: '535-5.7.8 Username and Password not accepted. Learn more at\n' + '535 5.7.8 BadCredentials g16-20020a05600c4ed000b003974860e15esm21842702wmq.40 - gsmtp', responseCode: 535, command: 'AUTH PLAIN' }
原有问题代码
email.js 邮件配置代码
const nodemailer = require('nodemailer'); const sendEmail = async (options) => { //1 创建邮件传输实例 const transporter = nodemailer.createTransport({ service: 'Gmail', host: process.env.EMAIL_HOST, port: process.env.EMAIL_PORT, auth: { user: process.env.EMAIL_USERNAME, pass: process.env.EMAIL_PASSWORD, }, }); //2 定义邮件配置项 const mailOptions = { from: 'Farhan Ajmal <18251598-146@uog.edu.pk>', to: options.email, subject: options.subject, text: options.message, }; //3 发送邮件 await transporter.sendMail(mailOptions); }; module.exports = sendEmail;
authController.js 接口逻辑代码
const jwt = require('jsonwebtoken'); const { promisify } = require('util'); const User = require('../models/userModel'); const catchAsync = require('../utils/catchAsync'); const AppError = require('../utils/appError'); const sendEmail = require('../utils/email'); exports.forgotPassword = catchAsync(async (req, res, next) => { //根据提交的邮箱查询用户 const user = await User.findOne({ email: req.body.email }); if (!user) { next(new AppError('There is no user with that email address ', 404)); } //发送邮件 try { await sendEmail({ email: user.email, subject: 'Your password is only valid for 10 min', message, }); res.sendStatus(200); } catch (err) { console.log(err); return next( new AppError('There was an error sending the email. Try again later'), 500 ); } });
问题根因
- 核心配置冲突:创建Nodemailer传输实例时同时配置了
service: 'Gmail'和自定义host/port,Nodemailer识别到service字段后会自动忽略自定义的host、port参数,强制连接Gmail官方SMTP服务器,根本没有对接预期的Mailtrap服务。错误日志里出现的gsmtp标识就是Gmail SMTP的返回,和Mailtrap无关。 - 凭据配置错误风险:即使修正配置冲突,如果环境变量里填的是Mailtrap注册账号密码、或者个人邮箱账号密码,也会触发认证失败——Mailtrap的SMTP服务需要使用对应测试收件箱生成的随机专属用户名、密码,和平台登录凭据不通用。
- 代码逻辑漏洞:
- 查询不到对应用户时,
next()调用后没有加return,代码会继续向下执行到发邮件逻辑,触发空指针错误 - 调用
sendEmail时传入了未定义的message变量,就算SMTP配置正确也会直接抛出语法错误 AppError实例化时500状态码写在了next参数的括号外,错误状态码不会被正确识别
- 查询不到对应用户时,
修复步骤
- 修正邮件传输实例配置,删除冲突的
service: 'Gmail'字段,确保Nodemailer连接Mailtrap服务器:
const transporter = nodemailer.createTransport({ host: process.env.EMAIL_HOST, // 填写Mailtrap后台给出的SMTP地址,通常为smtp.mailtrap.io port: Number(process.env.EMAIL_PORT), // 填写Mailtrap后台给出的对应端口,支持25、465、587、2525 auth: { user: process.env.EMAIL_USERNAME, // 填写Mailtrap测试收件箱生成的专属SMTP用户名 pass: process.env.EMAIL_PASSWORD, // 填写Mailtrap测试收件箱生成的专属SMTP密码 }, });
- 补全接口逻辑漏洞,修正后的
forgotPassword方法参考:
exports.forgotPassword = catchAsync(async (req, res, next) => { // 根据提交的邮箱查询用户 const user = await User.findOne({ email: req.body.email }); if (!user) { // 加return终止后续逻辑执行 return next(new AppError('There is no user with that email address ', 404)); } // 生成密码重置token(替换为项目里的实际token生成逻辑) const resetToken = user.createPasswordResetToken(); await user.save({ validateBeforeSave: false }); const resetURL = `${req.protocol}://${req.get('host')}/api/v1/users/resetPassword/${resetToken}`; // 定义邮件正文内容,修复message未定义问题 const message = `Forgot your password? Submit a PATCH request with your new password to: ${resetURL}.\nIf you didn't request a password reset, please ignore this email.`; try { await sendEmail({ email: user.email, subject: 'Your password reset token is only valid for 10 min', message, }); // 修正响应格式 res.status(200).json({ status: 'success', message: 'Reset token has been sent to your email' }) } catch (err) { console.log(err); // 发信失败时清除生成的重置token user.passwordResetToken = undefined; user.passwordResetExpires = undefined; await user.save({ validateBeforeSave: false }); return next( new AppError('There was an error sending the email. Try again later', 500) ); } });
- 核对环境变量配置:打开项目根目录的
.env文件,确认EMAIL_HOST、EMAIL_PORT、EMAIL_USERNAME、EMAIL_PASSWORD四个参数和Mailtrap后台给出的SMTP信息完全一致,没有多余空格、拼写错误,确认服务启动时环境变量被正确加载。
内容的提问来源于stack exchange,提问作者Farhan9690
相关产品推荐
相关产品推荐

