Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Gmail API for sending mails in Node.js

Tags:

Disclaimer:

  • I have followed Google's own Node.js quickstart guide and successfully connect and use the gmail.users.labels.list() functionality.
  • I have checked for questions/answers here, like this one (that is not using the Node.js API I am asking about), or this one (similar to this one) which apparently is the same problem I have but the solution does not work.

My problem:

When using Google's Node.js API I get a error trying to send a email. The error is:

{     "code": 403,     "errors": [{         "domain": "global",         "reason": "insufficientPermissions",         "message": "Insufficient Permission"     }] } 

My setup:

fs.readFile(secretlocation, function processClientSecrets(err, content) {     if (err) {         console.log('Error loading client secret file: ' + err);         return;     }     authorize(JSON.parse(content), sendMessage); });  function sendMessage(auth) {     var raw = makeBody('[email protected]', '[email protected]', 'subject', 'message test');     gmail.users.messages.send({         auth: auth,         userId: 'me',         message: {             raw: raw         }     }, function(err, response) {         res.send(err || response)     }); } 

The function processClientSecrets is from the Google guide i mentioned above. It reads my .json file that has my access_token and refresh_token. The makeBody function is a to make a encoded body message.

In the config variabels I have also:

var SCOPES = [     'https://mail.google.com/',     'https://www.googleapis.com/auth/gmail.modify',     'https://www.googleapis.com/auth/gmail.compose',     'https://www.googleapis.com/auth/gmail.send' ]; 

Why it should work:

  • the authorization process works for the gmail.users.labels.list() method.
  • the message body I'm testing works if I test it at Google's test page.

My question:

Is my setup wrong? Have there been changes in the API? What am I missing?

like image 511
Sergio Avatar asked Dec 31 '15 11:12

Sergio


2 Answers

Ok, so I found the problem(s).

Problem #1 While following the Node.js quickstart guide the example in that tutorial has

var SCOPES = ['https://www.googleapis.com/auth/gmail.readonly']; 

And when I got the .json that looks like:

{     "access_token": "xxx_a_long_secret_string_i_hided_xxx",     "token_type": "Bearer",     "refresh_token": "xxx_a_token_i_hided_xxx",     "expiry_date": 1451721044161 } 

those tokens where produced taking into account only the auth/gmail.readonly scope in the tutorial code.

So I deleted the first .json, added the scopes from my final scope array (i posted in the question) and ran the tutorial setup again, receiving a new token.

Problem #2

In the object passed to the API I was sending:

{     auth: auth,     userId: 'me',     message: {         raw: raw     } } 

but that is wrong, message key should be called resource.


Final setup:

This is what I added to the tutorial's code:

function makeBody(to, from, subject, message) {     var str = ["Content-Type: text/plain; charset=\"UTF-8\"\n",         "MIME-Version: 1.0\n",         "Content-Transfer-Encoding: 7bit\n",         "to: ", to, "\n",         "from: ", from, "\n",         "subject: ", subject, "\n\n",         message     ].join('');      var encodedMail = new Buffer(str).toString("base64").replace(/\+/g, '-').replace(/\//g, '_');         return encodedMail; }  function sendMessage(auth) {     var raw = makeBody('[email protected]', '[email protected]', 'test subject', 'test message');     gmail.users.messages.send({         auth: auth,         userId: 'me',         resource: {             raw: raw         }     }, function(err, response) {         res.send(err || response)     }); } 

And call everything with:

fs.readFile(secretlocation, function processClientSecrets(err, content) {     if (err) {         console.log('Error loading client secret file: ' + err);         return;     }     // Authorize a client with the loaded credentials, then call the     // Gmail API.     authorize(JSON.parse(content), sendMessage); }); 
like image 180
Sergio Avatar answered Sep 20 '22 18:09

Sergio


So for anyone looking at this trying to get a test email sent from their API but cant get this work heres what you gotta do:

Step 1: Replace the

var SCOPES = ['https://www.googleapis.com/auth/gmail.readonly']; 

with this:

var SCOPES = [     'https://mail.google.com/',     'https://www.googleapis.com/auth/gmail.modify',     'https://www.googleapis.com/auth/gmail.compose',     'https://www.googleapis.com/auth/gmail.send' ]; 

Step 2: At the end of googles sample code add this:

function makeBody(to, from, subject, message) {     var str = ["Content-Type: text/plain; charset=\"UTF-8\"\n",         "MIME-Version: 1.0\n",         "Content-Transfer-Encoding: 7bit\n",         "to: ", to, "\n",         "from: ", from, "\n",         "subject: ", subject, "\n\n",         message     ].join('');      var encodedMail = new Buffer(str).toString("base64").replace(/\+/g, '-').replace(/\//g, '_');         return encodedMail; }  function sendMessage(auth) {     var raw = makeBody('[email protected]', '[email protected]', 'This is your subject', 'I got this working finally!!!');     const gmail = google.gmail({version: 'v1', auth});     gmail.users.messages.send({         auth: auth,         userId: 'me',         resource: {             raw: raw         }          }, function(err, response) {         return(err || response)     }); }  fs.readFile('credentials.json', function processClientSecrets(err, content) {     if (err) {         console.log('Error loading client secret file: ' + err);         return;     }     // Authorize a client with the loaded credentials, then call the     // Gmail API.     authorize(JSON.parse(content), sendMessage); }); 

Step 3(Optional)

Delete this line:

authorize(JSON.parse(content), listLabels); 

And these:

/**  * Lists the labels in the user's account.  *  * @param {google.auth.OAuth2} auth An authorized OAuth2 client.  */  function listLabels(auth) {    const gmail = google.gmail({version: 'v1', auth});    gmail.users.labels.list({      userId: 'me',    }, (err, res) => {      if (err) return console.log('The API returned an error: ' + err);      const labels = res.data.labels;      if (labels.length) {        console.log('Labels:');        labels.forEach((label) => {          console.log(`- ${label.name}`);        });      } else {        console.log('No labels found.');      }    });  } 

(So you don't get the random labels in your console)

like image 32
Vincent Morris Avatar answered Sep 21 '22 18:09

Vincent Morris