NodeJS Googleapis Service Account authentication

Viewed 989

I'm trying to perform authentication on GoogleAPIs using a Service Account. I have a service account set up, with its credentials located at credentials.json. I try to access a private sheet, to which I added the E-Mail address of the service account with editing rights.

Here the code I am using:

const {
    google
} = require('googleapis');
const fs = require('fs');

let scopes = ['https://www.googleapis.com/auth/spreadsheets'];
let credentials = require("./credentials.json");

const authClient = new google.auth.JWT(
    credentials.client_email,
    null,
    credentials.private_key,
    scopes);

authClient.authorize(function(err, tokens) {
    if (err) {
        console.log(err);
        return;
    } else {
        authClient.setCredentials(tokens);
    }
});

const sheets = google.sheets({
    version: 'v4',
    authClient
});

let spreadsheetId = //...
let range = //...

const request = {
    spreadsheetId: spreadsheetId,
    range: range
};

sheets.spreadsheets.values.get(request, function(err, response) {
    if (err) {
        console.log('The API returned an error: ' + err);
    } else {
        console.log('Result: ' + response);
    }
});

I guess the API changed over time, since many guides showed different approaches, and in the end none worked for me. The error is as follows:

The API returned an error: Error: The request is missing a valid API key.

To my understanding, a simple API key should only be necessary for unauthenticated access on public sheets, so I don't get why it is even requiring that. If I add such an API key I get the error

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

Using

$ npm list googleapis
`-- googleapis@52.1.0

Any help would be greatly appreciated.

1 Answers

For who still facing googleapis problems within NodeJS Runtime in 2022.

  • Firstly, redirect into Google-IAM-Admin/ServiceAccount to pick the current working project.
  • Secondly, click to jump into Service Account that has the following format project@sub-name-id.iam.gserviceaccount.com.
  • Thirdly, between [Details, Permissions, Keys, Metrics, Logs]. Jump into Keys then Add Key -> Create new Key -> Key type::JSON and save JSON file to your computer.

Here within NodeJS Runtime, I use the following Semantic Version googleapis@100.0.0

  • You can create JWT Client and inject into google default auth at google.options({auth: client}); or provide auth-client to specific Service as google.chat({version: 'v1', auth: client});
  • However, in the following example. I create a GoogleAuth instance and then make an AuthClient after. Which resulted the same behaviour to the JWT Method.
/** Import Node Native Dependencies !*/
import * as path from "path";

/** Import ES6 Default Dependencies !*/
import {google} from "googleapis";

const {client_email, private_key} = require('$/keys/credentials.json');

/**
 ** @description - Google [[Service Account]] Authenticator.
 **/
const auth = new google.auth.GoogleAuth({
    keyFile: path.resolve('keys/credentials.json'),
    /** Scopes can be specified either as an array or as a single, space-delimited string; ~!*/
    scopes: [
        "https://www.googleapis.com/auth/chat.bot",
    ],
});

const client = new google.auth.JWT({
    email: client_email,
    key: private_key,
    /** Scopes can be specified either as an array or as a single, space-delimited string; ~!*/
    scopes: [
        "https://www.googleapis.com/auth/chat.bot",
    ],
});

(async () => {
    /** @description - Either [[Get Client]] from [Google Auth] or Use directly from [JWT Client] ~!*/
    const client = await auth.getClient();
    /** @description - Use this Authorized Client as Default Authenticated to fallback from [Non-Authenticated Services] ~!*/
    google.options({auth: client});

    const chat = google.chat({
        version: 'v1',
        /** @description - Provide [Authenticated Services] to [Google Chat Service] Instance ~!*/
        auth: client,
    });

    const response = await chat.spaces.members.get({
        // Required. Resource name of the attachment, in the form "spaces/x/messages/x/attachments/x".
        name: 'spaces',
    });
    console.log('response', response.data);

    return void 0;
})();
Related