11

I'm attempting to build an email parser that responds to a gmail pubsub message. I am currently able to receive and parse the pubsub message and extract the historyId, but I'm having trouble authenticating my request to the gmail api. Here's what I have up to now:

  //function defined outside the scope of the main function
  //used to create auth client
  function getAuthClient(event, callback) {
    var GoogleAuth = require('google-auth-library');

    var authFactory = new GoogleAuth();

    authFactory.getApplicationDefault(function (err, authClient) {
     if (err) {
       console.log('Unable to get application default credentials.');
       callback(err);
       return;
     }

     // Create the oAuth scopes you need
     if (authClient.createScopedRequired && authClient.createScopedRequired()) {
    console.log('creating scoped client');
      authClient = authClient.createScoped([
          'https://mail.google.com/'
      ]);
    }
   callback(null, authClient);
  });
}

exports.gmailParser = function gmailParser (event, callback) {
 var path = require('path');
 var base64 = require('base-64');

 // Set your service account key file into the environment for the auth lib 
to pick up
 process.env['GOOGLE_APPLICATION_CREDENTIALS'] = path.resolve(__dirname, 
'auth_credentials.json');
 console.log(process.env['GOOGLE_APPLICATION_CREDENTIALS']);


 console.log(__dirname);

 //parse pubsub message
 var pubsubMessage = event.data;
 var baseMessage = pubsubMessage.data;
 var decodedMessage = base64.decode(baseMessage);
 var messageJSON = JSON.parse(decodedMessage);

 // Extract emailAddress and historyId from gmail pubsub message
 var historyId = messageJSON.historyId;
 var email = messageJSON.emailAddress;

 getAuthClient(null, function(err, authClient) {

   //import necessary libraries
   var google = require('googleapis');
   var gmail = google.gmail('v1');

   if(err) {
     callback(err);
   }

  // Construct a params object
  var params = {
    userId: email,
    startHistoryId: historyId
 };


//Attempt to call gmail api. This is where the error occurs.
gmail.users.history.list(params, function(error, response) {
  if (error) {  
    console.log('Encountered error', error);
    callback(error);
  } else {
    console.log('Response', response);
    callback(response);
  }
});
 });
};

The code runs successfully but I get the following error:

"Error: Login Required
at Request._callback (/user_code/node_modules/googleapis/node_modules/google-auth-library/lib/transporters.js:85:15)
at Request.self.callback (/user_code/node_modules/googleapis/node_modules/request/request.js:188:22)
at emitTwo (events.js:106:13)
at Request.emit (events.js:191:7)
at Request.<anonymous> (/user_code/node_modules/googleapis/node_modules/request/request.js:1171:10)
at emitOne (events.js:96:13)
at Request.emit (events.js:188:7)
at IncomingMessage.<anonymous> (/user_code/node_modules/googleapis/node_modules/request/request.js:1091:12)
at IncomingMessage.g (events.js:291:16)
at emitNone (events.js:91:20)"   

I've tried adding the authClient to the param object but it returns a "Bad request" error. I've tried changing the order of the imports, created new credentials, but I haven't been able to get anywhere. If anyone has any pointers it would be greatly appreciated.

kevinivan05
  • 354
  • 4
  • 13
  • Since you'ure using Gmail API and NodeJS, did you try using the Login flow in [Gmail Node.js Quickstart](https://developers.google.com/gmail/api/quickstart/nodejs) – ReyAnthonyRenacia May 01 '17 at 15:04
  • I did, but when I try using the json file, I get the following error: Error: The incoming JSON object does not contain a client_email field – kevinivan05 May 01 '17 at 23:10
  • @kevinivan05 That means there is something wrong with your json file. Can you make sure you are using the correct file, attained by following the steps in the quickstart? The json file should look like this `{"installed":{"client_id":"your_id","project_id":"your_project_name","auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://accounts.google.com/o/oauth2/token","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","client_secret":"your_secret","redirect_uris":["urn:ietf:wg:oauth:2.0:oob","http://localhost"]}}` – Jon Church May 12 '17 at 01:30
  • Have you got the solution? – Ajay Sivan Jun 13 '18 at 08:47
  • 1
    It looks like your callback never used its `authClient`. Did you try replacing `google.gmail('v1');` with something like `google.gmail({version:'v1', auth: authClient);`? – Adam Bliss Jul 18 '18 at 02:15
  • That's an interesting comment...at this moment I don't have access to the code but I'll make a note of trying this out as soon as I get a chance! – kevinivan05 Jul 18 '18 at 02:28

1 Answers1

2

This blogpost might help. It has details regarding getting OAuth tokens using nodejs and Cloud Functions. I tried it and it worked fine.

https://cloud.google.com/blog/products/application-development/adding-custom-intelligence-to-gmail-with-serverless-on-gcp

Code: https://github.com/GoogleCloudPlatform/cloud-functions-gmail-nodejs

The below code snippet requests an OAuth 2.0 authorisation code

exports.oauth2init = (req, res) => {
  // Define OAuth2 scopes
  const scopes = [
    'https://www.googleapis.com/auth/gmail.modify'
  ];

  // Generate + redirect to OAuth2 consent form URL
  const authUrl = oauth.client.generateAuthUrl({
    access_type: 'offline',
    scope: scopes,
    prompt: 'consent' // Required in order to receive a refresh token every time
  });
  return res.redirect(authUrl);
};

The below code snippet gets an access token from the OAuth authorisation code

exports.oauth2callback = (req, res) => {
  // Get authorization code from request
  const code = req.query.code;

  // OAuth2: Exchange authorization code for access token
  return new Promise((resolve, reject) => {
    oauth.client.getToken(code, (err, token) =>
      (err ? reject(err) : resolve(token))
    );
  })
Kannappan Sirchabesan
  • 1,353
  • 11
  • 21
  • Is it also possible to get a refresh token by using the existing access token that we got at the time of sign inside the app? Any code examples or articles, please. – Shajeel Afzal Jul 11 '20 at 22:53