Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get nodemailer to work with 2LO for service accounts?

I have followed the instructions on the nodemailer site to the letter, but OAuth2 for google service accounts simply does not work for me.

Either I get ECONN timeouts when setting "host" to mail.google.com or some combination of "401, user/pwd not accepted, can not create access token" errors when using smtp.gmail.com as the host.

  • I've used this as my template: http://nodemailer.com/smtp/oauth2/#example-4
  • I've set up a service account: https://developers.google.com/identity/protocols/OAuth2ServiceAccount
  • I've enabled the gmail api.
  • I've created tokens that validate: https://www.googleapis.com/oauth2/v1/tokeninfo
  • I have also tried the xoauth2 npm package to generate tokens and failed...
  • I also tried the answer in this question, but still no luck...

There seems to be an endless supply of answers for 3LO, but none that I've tried for 2LO that work. Now, having said all that.

var nodemailer = require("nodemailer");
var { google } = require("googleapis");

var accessToken;
var expires;
var key = require(“path/to/secrets.json");
var privateKey = key.private_key;

var jwtClient = new google.auth.JWT(key.client_email, null, key.private_key, ["https://mail.google.com/"], null);

jwtClient.authorize(function(err, tokens) {
    if (err) {
        return;
     } else {
    token = tokens
    accessToken = tokens.access_token //this **IS** a valid token
    expires = tokens.expiry_date
     }

 var transporter = nodemailer.createTransport({
        host: "smtp.gmail.com",
        port: 465,
        secure: true,
        auth: {
            type: "OAuth2",
            user: key.client_email, //I've also used my email here
            serviceClient: key.client_id,
            privateKey: privateKey,
            accessToken: accessToken,
            expires: expires,
        },
    });

var mailOptions = {
    from: “[email protected]”
    to: “[email protected]",
    subject: "Ready",
    text: “Really Ready"
    }

transporter.sendMail(mailOptions, function(error, info) {
    if (error) {
        return;
    }
    console.log("Message %s sent: %s", info.messageId, info.response);
});
});

which generated the error:

535-5.7.8 Username and Password not accepted.

But as I mentioned, I've tried differing configurations and settings and gotten just as many different errors...

SO... Has anyone had success in using service accounts for 2LO using nodemailer?

I'm using node 9.5.0 and nodemailer ^4.6.0

like image 349
Lance Massey Avatar asked Nov 08 '22 10:11

Lance Massey


1 Answers

I got it working (2021!), these were the steps:

  1. Log in to console.- https://console.cloud.google.com/
  2. Create a service account under the project.
  3. Click on the new service account, go to permissions and add a member. You will use this member's email address when sending the request.
  4. Create keys for the service account. - keys -> add key. https://console.cloud.google.com/iam-admin/serviceaccounts
  5. Download your key file. You will get something like service-account-name-accountid.json. It will have all the information you need to get the code below running.
  6. Delegate authority to your service account https://developers.google.com/identity/protocols/oauth2/service-account#delegatingauthority. Addhttps://mail.google.com/ as the scope.
  7. Write some code like below:

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!');
like image 177
raptoria7 Avatar answered Nov 15 '22 11:11

raptoria7