Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Authenticating Google Sheet API on Heroku using NodeJS

I'm following this example to access the Google Sheets API:

https://developers.google.com/sheets/api/quickstart/nodejs

Within the example code is the following method to fetch a new oauth token.

function getNewToken(oauth2Client, callback) {
  var authUrl = oauth2Client.generateAuthUrl({
    access_type: 'offline',
    scope: SCOPES
  });
  console.log('Authorize this app by visiting this url: ', authUrl);
  var rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
  });
  rl.question('Enter the code from that page here: ', function(code) {
    rl.close();
    oauth2Client.getToken(code, function(err, token) {
      if (err) {
        console.log('Error while trying to retrieve access token', err);
        return;
      }
      oauth2Client.credentials = token;
      storeToken(token);
      callback(oauth2Client);
    });
  });
}

This works fine on my local machine (manually visiting the page as prompted in the terminal, and entering the code in the command line). But this seems unpractical and doesn't work on Heroku. Is there any way to automate this? Maybe by fetching the URL (and token) in the nodeJS application and storing this somehow?

Thanks in advance.

like image 315
peerbolte Avatar asked Jan 03 '23 11:01

peerbolte


1 Answers

Ok, so I ended up using a Service Account Key which can be generated at https://console.developers.google.com. This will generate a JSON file of which you need two values: private_key and client_email.

To test this locally you can download the dotenv npm module which will allow you to store Environment variables in a .env file in your project root. Your .env file will look like this:

GOOGLE_PRIVATE_KEY=<your-key-here-withouth-quotes>
GOOGLE_CLIENT_EMAIL=<your-email-here-withouth-quotes>

Don't forget to add the .env file to your .gitignore list when deploying your heroku app via git.

My auth.js file looks like this:

const GoogleAuth = require('google-auth-library');

const SCOPES = ['https://www.googleapis.com/auth/spreadsheets.readonly'];

function authorize() {
    return new Promise(resolve => {
        const authFactory = new GoogleAuth();
        const jwtClient = new authFactory.JWT(
            process.env.GOOGLE_CLIENT_EMAIL,
            null,
            process.env.GOOGLE_PRIVATE_KEY.replace(/\\n/g, '\n'), 
            SCOPES
        );

        jwtClient.authorize(() => resolve(jwtClient));
    });
}

module.exports = {
    authorize,
}

Note the replace function behind the private key variable.

My app.js (main file) looks like this:

require('dotenv').config();
const google = require('googleapis');
const sheetsApi = google.sheets('v4');
const googleAuth = require('./auth');

const SPREADSHEET_ID = 'Your-spreadsheet-ID';

googleAuth.authorize()
    .then((auth) => {
        sheetsApi.spreadsheets.values.get({
            auth: auth,
            spreadsheetId: SPREADSHEET_ID,
            range: "'Tab Name'!A1:H300",
        }, function (err, response) {
            if (err) {
                console.log('The API returned an error: ' + err);
                return console.log(err);
            }
            var rows = response.values;
            console.log(null, rows);
        });
    })
    .catch((err) => {
        console.log('auth error', err);
    });

If you get the following error:

The API returned an error: Error: The caller does not have permission

Share the spreadsheet you are trying to load with the google_client_email and try again.

If everything works locally, add the Environment Variables to your heroku app by visiting your heroku account and going to settings and click reveal config vars and deploy the application. If all goes well, you should have access to the document.

like image 142
peerbolte Avatar answered Jan 06 '23 23:01

peerbolte