Gmail API for sending mails in Node.js
Asked Answered
A

2

25

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?

Annexation answered 31/12, 2015 at 11:52 Comment(8)
Make sure you have the required scopes authorized: developers.google.com/gmail/api/v1/reference/users/messages/…Myth
@Myth I do, but still doesn't work, thanks for pointing. I had to have it also when testing in the Google tool. Currently I have these Auth scopes held: https://mail.google.com/, gmail.compose, gmail.modify, gmail.sendAnnexation
Go to the Oauth Playground, authorize with the Gmail Scopes, and take the access token and refresh token and put these in your .json-file. Do you still get an error?Yoohoo
@Yoohoo thanks for checking this out. Did that step you suggested and now the error is different: {"code":400,"errors":[{"domain":"global","reason":"invalidArgument","message":"'raw' RFC822 payload message string or uploading message via /upload/* URL required"}]}Annexation
@Annexation Hmm, that's annoying. Have you tried making the raw-string url safe (replace all + with - and all / with _)?Yoohoo
@Annexation Darn :( Then I don't know. Hopefully someone else can chime in.Yoohoo
@Yoohoo found my problem(s). Thanks for checking this!Annexation
@Annexation Great! No problem. :)Yoohoo
A
31

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);
});
Annexation answered 2/1, 2016 at 7:14 Comment(8)
how to attach html payload?Holcombe
@ArjunKava just set Content-Type: text/html; instead of Content-Type: text/plain;.Boyle
thank u @JyotmanSingh but i also need to send attachments within payload which are my local PC files , i have tried with mime content type but still not working.Holcombe
Please, can you explain why you need to do some replacements over the base64 raw message. ThanksVotaw
I also would like to know why they replacements of the base64 content. Also, where did you find documentation about raw content is inside resource attribute.Gam
this code has given me an this error : 'raw' RFC822 payload message string or uploading message via /upload/* URL required [400].. This can be fixed by updating google-auth-api to the latest verstion. github.com/google/google-api-nodejs-client/issues/979Bazaar
You forgot const gmail = google.gmail({version: 'v1', auth}); in sendMessage(). Also res in sendMessage() was not defined. After commenting it out, I got this error: Error: Insufficient Permission. Anyone else having this issue?Tensive
hello, How can I get an inbox email list? please advice. I already fetched list labelsSwaraj
D
8

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)

Dubrovnik answered 18/7, 2019 at 4:15 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.