Search code examples
node.jssmtpgmailnodemailergoogle-workspace

GSuite Error: Can't create new access token for user - OAuth2 Nodemailer


I'm trying to send emails through gmail from nodejs but I keep running into this error.

Error: Can't create new access token for user
  at XOAuth2.generateToken(c: \apps\node\myapp\node_modules\nodemailer\lib\xoauth2\index.js: 184: 33)
  at XOAuth2.getToken(c: \apps\node\myapp\node_modules\nodemailer\lib\xoauth2\index.js: 123: 18)
  at SMTPConnection._handleXOauth2Token(c: \apps\node\myapp\node_modules\nodemailer\lib\smtp - connection\index.js: 1697: 27)
  at SMTPConnection.login(c: \apps\node\myapp\node_modules\nodemailer\lib\smtp - connection\index.js: 540: 22)
  at c: \apps\node\myapp\node_modules\nodemailer\lib\smtp - transport\index.js: 374: 32
  at SMTPConnection.<anonymous>(c: \apps\node\myapp\node_modules\nodemailer\lib\smtp - connection\index.js: 215: 17)
  at Object.onceWrapper(events.js: 420: 28)
  at SMTPConnection.emit(events.js: 314: 20)
  at SMTPConnection._actionEHLO(c: \apps\node\myapp\node_modules\nodemailer\lib\smtp - connection\index.js: 1313: 14)
  at SMTPConnection._processResponse(c: \apps\node\myapp\node_modules\nodemailer\lib\smtp - connection\index.js: 942: 20)
  at SMTPConnection._onData(c: \apps\node\myapp\node_modules\nodemailer\lib\smtp - connection\index.js: 749: 14)
  at TLSSocket.SMTPConnection._onSocketData(c: \apps\node\myapp\node_modules\nodemailer\lib\smtp - connection\index.js: 195: 44)
  at TLSSocket.emit(events.js: 314: 20)
  at addChunk(_stream_readable.js: 304: 12)
  at readableAddChunk(_stream_readable.js: 280: 9)
  at TLSSocket.Readable.push(_stream_readable.js: 219: 10) {
  code: 'EAUTH',
    command: 'AUTH XOAUTH2'
}

I have set up my G Suite account, created a service account and enabled the Gmail API for that project. I have also enabled G-Suite domain wide delegation for the service account and added the client ID and the proper scope to my G Suite account.

I downloaded my credentials file and renamed it to use .js so I could import it as an ES module.

import nodemailer from "nodemailer";
import * as key from "../credentials.js";

const SENDER_EMAIL = "[email protected]";
const RECEIVER_EMAIL = "[email protected]";

async function start() {
  const transporter = nodemailer.createTransport({
    host: "smtp.gmail.com",
    port: 465,
    secure: true,
    auth: {
      type: "OAuth2",
      user: SENDER_EMAIL,
      serviceClient: key.client_id,
      privateKey: key.private_key,
    },
  });

  try {
    await transporter.verify();
    await transporter.sendMail({
      from: SENDER_EMAIL,
      to: RECEIVER_EMAIL,
      subject: "Hello Humans",
      text: "The quick brown fox jumps over the lazy dog.",
    });
  } catch (err) {
    console.error(err);
  }
}

export default start;

Now, calling start() errors out with the above message. I can't figure it out. Help.


Solution

  • I hope you solved your issue 11 months ago but, for those who find this, I had the same issue and found the answer here. I believe this might be your case specially because you mentioned that hardcoding the value does work.

    Apparently, the \n in the private key brings some issues when used this way. In my case, I'm taking the private key value from Firebase runtime config, and following suggestions in that link I ended up with a replace(/\\n/g, '\n') on the key:

    const nodemailerTransporter = nodemailer.createTransport({
      host: env.nodemailer.host,
      port: 465,
      secure: true, // true for TLS 465, false for other ports
      auth: {
        type: "OAuth2",
        user: env.nodemailer.user,
        serviceClient: env.nodemailer.clientid,
        privateKey: env.nodemailer.privatekey.replace(/\\n/g, '\n'),  // <=== HERE
        accessUrl: env.nodemailer.tokenuri, // <== HERE
      },
    });
    

    which does work. I'm using "host": "smtp-relay.gmail.com" but it's the same for your smtp.gmail.com case.

    Also consider the accessUrl (the token_uri as taken from credentials.json):

    "token_uri": "https://oauth2.googleapis.com/token"
    

    which is not the same as the default used by nodemailer (https://accounts.google.com/o/oauth2/token). IDK if that matters.