NodeMailer - 使用Google服务帐户发送邮件失败,因为“用户名和密码未被接受”

时间:2017-07-15 20:07:14

标签: node.js email google-oauth google-apps nodemailer

我正在创建一个Twitter机器人,我正在实施一种方法,如果出现错误,会向我发送电子邮件。由于我已经使用谷歌API访问谷歌驱动器(这里没有问题),我决定使用服务帐户发送电子邮件(谷歌控制台说它可以这样使用)

我到目前为止发送电子邮件的方法是:

var config = require('./config/mail');
var google = require('./config/google');
var nodemailer = require('nodemailer');

var send = function (args) {
  let transporter = nodemailer.createTransport({
    'service': 'gmail',
    'auth': {
        'type': 'OAuth2',
        'user': google.client_email,
        'serviceClient': google.client_id,
        'privateKey': google.private_key
    }
  });
  transporter.on('token', token => console.log(token));

  let message = {
    'from': `"${config.serverFromName}" <${config.serverFromMail}>`,
    'to': args.to,
    'subject': args.subject,
    'text': args.text,
    'html': `<p>${args.text}</p>`
  };

  transporter.sendMail(message, (err, info) => {
    if (err) {
      console.log('Mail couldn\'t be sent because: ' + err);
    } else {
      console.log('Mail sent');
    }
  });
};

config/google文件包含Google在您创建服务帐户时为您生成的数据。 config.serverFromNameconfig.serverFromMail是发件人的姓名和电子邮件(与服务帐户ID不同)。 args包含食谱电子邮件和内容

当我测试send方法时,我在控制台中收到以下消息:

Mail couldn't be sent because: Error: Invalid login: 535-5.7.8 Username and Password not accepted. Learn more at
535 5.7.8  https://support.google.com/mail/?p=BadCredentials z123sm543690vkd.10 - gsmtp

我知道令牌正在被正确创建,因为我创建的监听器正在打印它:

{ user: 'name@project.iam.gserviceaccount.com',
  accessToken: 'ya29.ElmIBLxzfU_kkuZeyISeuRBeljmAe7HNTlwuG4K12ysUNo46s-eJ8NkMYHQqD_JrqTlH3yheNc2Aopu9B5vw-ivEqvPR4sTDpWBOg3xUU_4XiJEBLno8FHsg',
  expires: 1500151434603 }

在互联网上搜索我发现它可能是OAuth范围的问题。但是,所有涉及它的信息都是指使用客户端ID,而不是服务帐户。我在Google开发者控制台中找不到该选项。

我做错了什么想法?

3 个答案:

答案 0 :(得分:2)

结论:Google描述服务帐户的具体方式与nodemailer不兼容。但有一种方法!

我在这同样的问题上花了无数个小时!我得出结论,谷歌的管理控制台间接地删除了一半这个功能。控制台不提供使用服务帐户第一次授权(用户接受同意屏幕)所需范围的方法。

首先,按照Google Drive API的Node.JS Quickstart说明授权范围并接收刷新令牌。

  1. 转到console.developers.google.com,构建OAuth2.0客户端ID,然后下载client_secret.json文件。
  2. 创建单独的临时模块文件夹并使用NPM下载google api模块

    npm install googleapis

    npm install google-auth-library

  3. 创建quickstart.js文件

  4. 将client_secret.json文件放在quickstart.js旁边

  5. quickstart.js中的第7行是用于定义您打算允许应用程序访问的范围的数组。根据需要进行修改。强烈建议仅为预期的内容提供访问权限。请参阅Gmail API Scopes

  6. RUN node quickstart.js

  7. 在浏览器中打开URL,进行身份验证,然后将代码从浏览器复制回终端窗口。这将下载一个nodejs-gmail-quickstart.json文件,该文件的位置将在 stdout 中提供。
    这是您无法为服务帐户完成的部分。此操作将SCOPES数组中提供的范围授权给下载的access_token&amp;刷新令牌。

  8. 注意:access_token的使用寿命为1小时。 refresh_token是不朽的。

    现在你有一个授权的refresh_token! 接下来是使用Nodemailer中的3LO设置您的auth对象。我会更多地看底部示例,因为并非所有值都是必需的。我的身份证明如下:

    const mailbot = nodemailer.createTransport({
          host: 'smtp.gmail.com',
          port: 587,              // TLS (google requires this port for TLS)
          secure: false,          // Not SSL
          requireTLS: true,       // Uses STARTTLS command (nodemailer-ism)
          auth: {
              // **HIGHLY RECOMMEND** ALL values be
              //  read in from a file not placed directly in code.  
              // Make sure that file is locked down to only the server daemon
              type : 'OAuth2',
              user : config.client_email,
              scope : "https://www.googleapis.com/auth/gmail.send",
              clientId : config.client_id,
              clientSecret: secret,
              refreshToken: activeToken.refresh_token
    
              // AT RUNTIME, it looks like this:
              //type : 'OAuth2',
              //user : 'user@gmail.com',   // actual user being impersonated
              //scope : "", //Optional, but recommend to define for the action intended
              //clientId : '888888888998-9xx9x99xx9x99xx9xxxx9xx9xx9x88x8xxx.apps.googleusercontent.com',
              //clientSecret: 'XxxxxXXxX0xxxxxxxx0XXxX0',
              //refreshToken: '1/XXxXxsss-xxxXXXXXxXxx0XXXxxXXx0x00xxx'              
          }
     });
    

    提示:Gmail会从使用授权用户帐户(用户模拟)发送的任何电子邮件中重写FROM字段。如果您想稍微自定义,请使用语法{ FROM: '"Display NAME" <user email>' },因为电子邮件匹配,它不会覆盖您的显示名称选择。

    注意:nodemailer将使用刷新令牌向https://accounts.google.com/o/oauth2/token发出令牌请求,以自动获取access_token。

    不幸的是,nodemailer缺乏将收到的令牌直接保存到文件中的功能,而只是使用this.emit()。如果服务器保持活动状态,则不会出现问题,但由于我的服务器只是爆破,因此每次都会请求新的access_token,因此总会产生延迟。

    [安全] 希望这适合您!松开私钥加密服务帐户2LO会带来令人失望,但至少这种客户端ID方式很难欺骗。我担心安全问题,但阅读更多我对这个实现很满意。请参阅Google Identity Platform(Nodemailer使用HTTP / REST详细信息)和给定

      

    [1] Google的OAuth 2.0端点位于   https://accounts.google.com/o/oauth2/v2/auth。这个端点是   只能通过HTTPS访问。纯HTTP连接被拒绝。

         

    [5] Web服务器收到授权码后,可以进行交换   访问令牌的授权码。

    您正在使用TLS最初连接授权代码,然后将其与您的客户端ID数据匹配,以及refresh_token(您必须经历我们上面所做的麻烦)然后您可以收到access_token以实际与Google API交互

    只要您将OAuth2.0客户端ID(高度随机的用户名),密码和刷新令牌保持为尽可能独立,安全和隐藏,就可以增加安全状态,您应该能够安然入睡。 好运!

答案 1 :(得分:1)

在访问了 OAuth 2.0 Playground 并尝试了 gmail-related sub-scopes 的所有可能变体之后,甚至完全选择了它们......

https://www.googleapis.com/auth/gmail.labels
https://www.googleapis.com/auth/gmail.send
https://www.googleapis.com/auth/gmail.readonly
https://www.googleapis.com/auth/gmail.compose
https://www.googleapis.com/auth/gmail.insert
https://www.googleapis.com/auth/gmail.modify
https://www.googleapis.com/auth/gmail.metadata
https://www.googleapis.com/auth/gmail.settings.basic
https://www.googleapis.com/auth/gmail.settings.sharing

...OP 标题中描述的错误消息仍然存在:

Error: Invalid login: 535-5.7.8 Username and Password not accepted

NodeMailer 似乎无法通过上述范围进行连接。事实上,它在其 OAuth2 SMTP transport docs

的“疑难解答”部分中明确提到 <块引用>

Gmail SMTP 的正确 OAuth2 范围是 https://mail.google.com/,请确保您的客户端在请求用户权限时设置了此范围

虽然这不仅仅是发送电子邮件,但它确实有效!

实现更细粒度的范围解决方案的唯一替代方法似乎是求助于谷歌自己的 Gmail API,您可以在生成 OAuth2 客户端时传递范围(当然至少应该包括授予的范围)显示 OAuth 同意屏幕的时间):

oAuth2Client.generateAuthUrl({
  access_type: 'offline',
  scope: SCOPES,
})

答案 2 :(得分:0)

我能够使用 Google 和 nodemailer 获得服务帐户:

这些是步骤:

  1. 登录控制台。- https://console.cloud.google.com/
  2. 在项目下创建一个服务帐户。
  3. 单击新的服务帐户,转到权限并添加成员。发送请求时,您将使用该成员的电子邮件地址。
  4. 为服务帐号创建密钥。 - 钥匙 -> 添加钥匙。 https://console.cloud.google.com/iam-admin/serviceaccounts
  5. 下载您的密钥文件。你会得到类似 service-account-name-accountid.json 的东西。它将包含运行以下代码所需的所有信息。
  6. 将权限委托给您的服务帐户 https://developers.google.com/identity/protocols/oauth2/service-account#delegatingauthority。添加https://mail.google.com/作为范围。
  7. 编写如下代码:

const nodemailer = require('nodemailer');
const json = require('./service-account-name-accountid.json');

const sendEmail = async (email, subject, text) => {
    try {

        const transporter = nodemailer.createTransport({
            host: 'smtp.gmail.com',
            port: 465,
            secure: true,
            auth: {
                type: 'OAuth2',
                user: email, //your permissioned service account member e-mail address
                serviceClient: json.client_id,
                privateKey: json.private_key
            }
        });

        await transporter.verify();
        
        await transporter.sendMail({
                from: json.service_email,
                to: email, //you can change this to any other e-mail address and it should work!
                subject,
                text
        });
        console.log('success!');
        return {
            status : 200
        }

    } catch (error) {
        console.log(error);
        return {
            status : 500,
            error
        }
    }
}

sendEmail('your_permissioned_service_account_email_address@some_place.com, 'testing 123', 'woohoo!');