Search code examples
node.jsgoogle-apinodemailer

How to Resolve Error Sending Email Using nodemailer googleapi?


Last week I initially posted this asking for help using nodemailer and googleapi. I'm trying to use nodemailer and googleapis to send an email. I have set up my project in https://console.cloud.google.com/ and have set my CLIENT_ID, CLIENT_SECRET, CLIENT_REDIRECT_URI and REFRESH_TOKEN in a .env and have confirmed that the values are being populated. In debug mode I have noticed the following error stack when I send the error:

     'Error: invalid_grant\n    at Gaxios._request (/Users/ENV/Tutoring-Invoice-Management-System/node_modules/gaxios/build/src/gaxios.js:130:23)\n    at processTicksAndRejections 
(node:internal/process/task_queues:96:5)\n    
at async OAuth2Client.refreshTokenNoCache (/Users/ENV/Tutoring-Invoice-Management-System/node_modules/google-auth-library/build/src/auth/oauth2client.js:174:21)\n    
at async OAuth2Client.refreshAccessTokenAsync (/Users/ENV/Tutoring-Invoice-Management-System/node_modules/google-auth-library/build/src/auth/oauth2client.js:198:19)\n    
at async OAuth2Client.getAccessTokenAsync (/Users/ENV/Tutoring-Invoice-Management-System/node_modules/google-auth-library/build/src/auth/oauth2client.js:227:23)\n    
at async sendMail (/Users/ENV/Tutoring-Invoice-Management-System/service/send-email.js:17:29)'

The code is below. I have edited it based on an answer to the question already. My question now is, why am I getting the invalid_grant error? Based on the formal documentation I have set everything up correctly in https://console.cloud.google.com/apis/credentials/oauthclient. But perhaps there is an issue there?

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.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.REDIRECT_URI);
console.log("oAuth2Client: " + oAuth2Client);

oAuth2Client.setCredentials({refresh_token: process.env.REFRESH_TOKEN})

async function sendMail() {
    try {
        const accessToken = await oAuth2Client.getAccessToken()
    
        const transport = nodemailer.createTransport({
          host: "smtp.gmail.com",
          port: 465,
          secure: true,
          auth: {
            type: 'OAuth2'
          }
        });
    
        const mailOptions = {
          from: 'envolonakis@gmail.com',
          to: 'envolonakis@gmail.com',
          subject: "Test Email API Subject",
          text: "Test Email API Text",
          html: "<h1> Test Email API HTML </h1>",
          auth: {
            user: process.env.OWNER_EMAIL,
            accessToken: accessToken.token
          }
        }
    
        const result = await transport.sendMail(mailOptions);
        return result;
      } catch (error) {
        console.log(error.stack);  
        return error;
      }
}

    
sendMail()

Solution

  • From the official documentation, this is what you need to use:

      try {
    
        const accessToken = await oAuth2Client.getAccessToken()
    
        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.RECIPIENT,
          subject: "Test Email API Subject",
          text: "Test Email API Text",
          html: "<h1> Test Email API HTML </h1>",
          auth: {
            user: process.env.OWNER_EMAIL,
            accessToken: accessToken.token
          }
        }
    
        const result = await transport.sendMail(mailOptions);
        return result;
      } catch (error) {
        return error;
      }
    

    One error that you have whilst using the google api authentication library is with the token. You were passing the complete token object to the auth configuration of nodemailer instead of just the access token string. Another thing to keep in mind, adding or removing parameters to the auth configuration of nodemailer will lead to different errors.