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

使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 23:44:08