Sergio
Sergio

Reputation: 28837

Gmail API for sending mails in Node.js

Disclaimer:

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:


My question:

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

Upvotes: 25

Views: 27562

Answers (3)

Chadi
Chadi

Reputation: 787

Also, I'd like to add something on top of the previous answers.

If you previously ran the examples with the gmail.readonly scopes, make sure to delete the token.json in order to redo the authentication with the new scopes.

Upvotes: 0

Vincent Morris
Vincent Morris

Reputation: 670

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)

Upvotes: 8

Sergio
Sergio

Reputation: 28837

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);
});

Upvotes: 31

Related Questions