You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

邮箱验证OTP功能异常:随机出现‘Token不存在’问题排查求助

邮箱验证功能随机出现「Token not found」错误的排查思路

现有实现代码

注册流程

const OTP = generateOTP();
const verificationToken = new VerificationToken({
  owner: newUser._id,
  token: OTP,
});
await verificationToken.save();

邮箱验证接口(verifyEmail)

exports.verifyEmail = async (req, res) => {
  const { userId, otp } = req.body;
  if (!userId || !otp.trim())
    return sendError(res, "Invalid request, missing parameters!");
  if (!isValidObjectId(userId)) return sendError(res, "Invalid user ID");
  const user = await User.findById(userId);

  if (!user) return sendError(res, "User not found!");

  if (user.verified) return sendError(res, "This account is already verified");

  const token = await VerificationToken.findOne({ owner: user._id });
  if (!token) return sendError(res, "Token not found!");
  const match = await token.compareToken(otp);
  if (!match) return sendError(res, "Please provide a valid code");
  user.verified = true;
  await user.save();
  await VerificationToken.deleteOne({ _id: token._id });
  res.json({
    success: true,
    message: "Your email is verified",
    user: { username: user.username, email: user.email, id: user._id },
  });
};

可能的原因及排查方向

  • Token过期机制未处理:检查VerificationToken模型是否设置了TTL索引或过期字段,如果Token到期后被自动清理,此时用户验证就会触发「Token not found」。建议在模型中明确过期规则,同时在验证接口中区分「Token过期」和「Token不存在」的提示,避免混淆。
  • 并发请求导致Token提前删除:用户多次点击验证按钮会触发并发请求,第一个请求验证通过后删除Token,后续请求就会查不到数据。可以用findOneAndUpdate给Token加排他锁,或者在接口中先检查用户是否处于验证中状态,避免重复处理。
  • Token保存失败未捕获异常:注册流程中verificationToken.save()可能因数据库波动、字段校验失败等原因保存失败,但代码未做异常捕获,导致用户收到OTP但数据库无对应Token。给save()添加try-catch块,捕获异常后终止流程并告知用户注册异常。
  • 重复注册导致Token关联错误:若User模型未给邮箱字段加唯一索引,用户重复注册会生成多个用户ID,若用户用旧ID验证,对应的Token可能已被新注册覆盖或清理。给User的邮箱字段添加唯一约束,避免重复创建用户。
  • 数据库查询一致性问题:MongoDB默认最终一致性,极端情况下Token刚保存,用户立即验证时,从副本库可能读不到最新数据。验证接口查询Token时指定读偏好为primary,确保从主库读取最新数据;或在注册保存Token后,额外执行一次查询确认Token存在再发送邮件。
  • 第三方操作误删Token:检查是否有定时清理脚本、其他接口操作VerificationToken集合,比如清理过期Token的脚本逻辑错误,误删未过期的记录。监控数据库该集合的删除操作日志,定位删除触发源。
  • 用户传递错误的userId:虽然接口校验了ObjectId格式,但用户可能因邮件链接参数错误、前端传递失误拿到无效userId,导致查询不到对应Token。建议将userId和OTP封装到邮件链接的参数中,避免用户手动输入,同时前端传递时做二次校验。

内容的提问来源于stack exchange,提问作者willm berchiche

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 17:33:32