【问题标题】:Why is OAuth2 with Gmail Nodejs Nodemailer producing "Username and Password not accepted" error为什么带有 Gmail Nodejs Nodemailer 的 OAuth2 会产生“不接受用户名和密码”错误
【发布时间】:2015-01-10 03:49:37
【问题描述】:

OAuth2 在尝试使用 Gmail+Nodejs+Nodemailer 发送电子邮件时产生“用户名和密码不被接受”错误

代码 - Nodejs - Nodemailer 和 xoauth2

var nodemailer = require("nodemailer");

var generator = require('xoauth2').createXOAuth2Generator({
    user: "", // Your gmail address.

    clientId: "",
    clientSecret: "",
    refreshToken: "",
});



// listen for token updates
// you probably want to store these to a db
generator.on('token', function(token){
    console.log('New token for %s: %s', token.user, token.accessToken);
});


// login
var smtpTransport = nodemailer.createTransport({
    service: 'gmail',
    auth: {
        xoauth2: generator
    }
});


var mailOptions = {
    to: "",
    subject: 'Hello ', // Subject line
    text: 'Hello world ', // plaintext body
    html: '<b>Hello world </b>' // html body
};


smtpTransport.sendMail(mailOptions, function(error, info) {
  if (error) {
    console.log(error);
  } else {
    console.log('Message sent: ' + info.response);
  }
  smtpTransport.close();
});

问题:

  • 我使用 Google OAuth2 Playground 创建令牌,https://developers.google.com/oauthplayground/

  • 它看起来可以使用 refreshToken 获取有效的 accessToken(即,它会在屏幕上打印新的访问令牌。)在尝试发送电子邮件之前不会出错。

  • 我添加了可选的 accessToken: 但得到了同样的错误。 ( "用户名和密码不被接受")

  • 我不是 100% 确定“用户名”,文档说它需要一个“用户”电子邮件地址 - 我猜是创建令牌的帐户的电子邮件,但不是 100% 清楚。我已经尝试了几件事,但都没有成功。

  • 我已经搜索了 gmail 帐户上的选项,没有发现任何看起来有问题的内容。

  • 另外,当我使用 Java 执行此操作时,它需要 google 用户 ID 而不是电子邮件地址,不知道为什么这是使用电子邮件地址而 Java 使用的是 UserId。

【问题讨论】:

    标签: oauth oauth-2.0 gmail google-apps nodemailer


    【解决方案1】:

    nodemailer 因“撰写”范围而失败

    问题在于“范围”

    它失败了: https://www.googleapis.com/auth/gmail.compose

    但是如果我使用它就可以了 https://mail.google.com/

    【讨论】:

    • 即使我使用了正确的范围,我也面临着类似的问题。您是否知道任何可能导致这种情况的问题? (我在这里解释了我的问题:stackoverflow.com/questions/19766912/…
    • @Abdel-RahmanShoman 我在 github 上的“Nodemailer”上留下了一个问题 - 他们说范围,这为我解决了问题。
    • 我们应该使用什么范围? @eddyparkinson
    • @ngLover 查看范围值 - Gmail API v1 - 这里是developers.google.com/oauthplayground
    • 当我只使用发送范围 (googleapis.com/auth/gmail.send) 时也发生了同样的情况,使用了推荐的范围并且一切顺利
    【解决方案2】:

    只需执行以下操作:

    1- 从这里获取 credentials.json 文件 https://developers.google.com/gmail/api/quickstart/nodejs 按启用 Gmail API,然后选择桌面应用程序

    2- 将此文件与您的凭据文件一起保存在某处

    const fs = require('fs');
    const readline = require('readline');
    const {google} = require('googleapis');
    
    // If modifying these scopes, delete token.json.
    const SCOPES = ['https://mail.google.com'];
    // The file token.json stores the user's access and refresh tokens, and is
    // created automatically when the authorization flow completes for the first
    // time.
    const TOKEN_PATH = 'token.json';
    
    // Load client secrets from a local file.
    fs.readFile('credentials.json', (err, content) => {
        if(err){
            return console.log('Error loading client secret file:', err);
        }
    
        // Authorize the client with credentials, then call the Gmail API.
        authorize(JSON.parse(content), getAuth);
    });
    
    /**
     * Create an OAuth2 client with the given credentials, and then execute the
     * given callback function.
     * @param {Object} credentials The authorization client credentials.
     * @param {function} callback The callback to call with the authorized client.
     */
    function authorize(credentials, callback) {
        const {client_secret, client_id, redirect_uris} = credentials.installed;
        const oAuth2Client = new google.auth.OAuth2(client_id, client_secret, redirect_uris[0]);
    
        // Check if we have previously stored a token.
        fs.readFile(TOKEN_PATH, (err, token) => {
            if(err){
                return getNewToken(oAuth2Client, callback);
            }
            oAuth2Client.setCredentials(JSON.parse(token));
            callback(oAuth2Client);
        });
    }
    
    /**
     * Get and store new token after prompting for user authorization, and then
     * execute the given callback with the authorized OAuth2 client.
     * @param {google.auth.OAuth2} oAuth2Client The OAuth2 client to get token for.
     * @param {getEventsCallback} callback The callback for the authorized client.
     */
    
    function getNewToken(oAuth2Client, callback) {
        const authUrl = oAuth2Client.generateAuthUrl({
            access_type: 'offline',
            scope: SCOPES,
        });
        console.log('Authorize this app by visiting this url:', authUrl);
        const rl = readline.createInterface({
            input: process.stdin,
            output: process.stdout,
        });
        rl.question('Enter the code from that page here: ', (code) => {
            rl.close();
            oAuth2Client.getToken(code, (err, token) => {
            if (err) return console.error('Error retrieving access token', err);
            oAuth2Client.setCredentials(token);
            // Store the token to disk for later program executions
            fs.writeFile(TOKEN_PATH, JSON.stringify(token), (err) => {
                if (err) return console.error(err);
                console.log('Token stored to', TOKEN_PATH);
            });
            callback(oAuth2Client);
            });
        });
    }
    
    function getAuth(auth){
    
    }

    3 - 通过在终端中输入来运行此文件:node THIS_FILE.js

    4- 你会有 token.json 文件

    5-从credentials.json和token.json中获取用户信息,填写到下面的函数中

    const nodemailer = require('nodemailer');
            const { google } = require("googleapis");
            const OAuth2 = google.auth.OAuth2;
    
            const email = 'gmail email'
            const clientId = ''
            const clientSecret = ''
            const refresh = ''
    
    
    
            const oauth2Client = new OAuth2(
                clientId,
                clientSecret,
            );
    
            oauth2Client.setCredentials({
                refresh_token: refresh
            });
            const newAccessToken = oauth2Client.getAccessToken()
    
    
            let transporter = nodemailer.createTransport(
                {
                    service: 'Gmail',
                    auth: {
                        type: 'OAuth2',
                        user: email,
                        clientId: clientId,
                        clientSecret: clientSecret,
                        refreshToken: refresh,
                        accessToken: newAccessToken
                    }
                },
                {
                    // default message fields
    
                    // sender info
                    from: 'Firstname Lastname <your gmail email>'
                }
            );
    
            const mailOptions = {
                from: email,
                to: "",
                subject: "Node.js Email with Secure OAuth",
                generateTextFromHTML: true,
                html: "<b>test</b>"
            };
    
            transporter.sendMail(mailOptions, (error, response) => {
                error ? console.log(error) : console.log(response);
                transporter.close();
            });

    【讨论】:

      【解决方案3】:

      如果你的问题是范围,这里有一些帮助来解决

      试图将此作为编辑添加到最佳答案,但它是rejected,真的不知道为什么这是题外话?

      在此处查看注释:https://nodemailer.com/smtp/oauth2/#troubleshooting

      如何修改作用域

      当您获得第一个 refresh_token 时,范围将被纳入授权步骤。如果您通过代码生成刷新令牌(例如使用Node.js sample),则需要在请求 authUrl 时设置修改后的范围。

      Node.js sample需要修改SCOPES

      // If modifying these scopes, delete token.json.
      -const SCOPES = ['https://www.googleapis.com/auth/gmail.readonly'];
      +const SCOPES = ['https://mail.google.com'];
      // The file token.json stores the user's access and refresh tokens, and is
      // created automatically when the authorization flow completes for the first
      // time.
      

      然后对oAuth2Client.generateAuthUrl 的调用将生成一个url,该url 将请求用户授权以接受完全访问。

      来自Node.js sample

      function getNewToken(oAuth2Client, callback) {
        const authUrl = oAuth2Client.generateAuthUrl({
          access_type: 'offline',
          scope: SCOPES,
        });
      

      【讨论】:

        猜你喜欢
        • 2019-06-10
        • 1970-01-01
        • 1970-01-01
        • 2014-11-26
        • 2017-09-10
        • 2013-11-21
        • 2020-09-29
        • 2022-08-12
        • 2023-03-14
        相关资源
        最近更新 更多