Google Sheets Api Authorization with a service account - node.js

I am using googleapis library v44.0.0. When I try to log in, I get an error.
Google Sheets API has not been used in project 33120758 before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/sheets.googleapis.com/overview?project=33120758 then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.
I use google-auth-library version ^ 0.10.0 on the same project. And this error was not.
const { google } = require("googleapis");
// ...
const JwtClient = new google.auth.JWT(
client_email,
null,
private_key,
["https://www.googleapis.com/auth/spreadsheets"]
);
await JwtClient.authorize();
google.options({ auth: JwtClient });
// ...
const client = google.sheets({ version: "v4" });
const data = await client.spreadsheets.get({ spreadsheetId: myId});

You want to use Sheets API with the service account.
You want to achieve this using googleapis with Node.js.
If my understanding is correct, how about this modification?
Modified script:
From:
await JwtClient.authorize();
google.options({ auth: JwtClient });
// ...
const client = google.sheets({ version: "v4" });
const data = await client.spreadsheets.get({ spreadsheetId: myId});
To:
const client = google.sheets({ version: "v4", "auth": JwtClient });
const data = await client.spreadsheets.get({ spreadsheetId: myId});
console.log(data.data)
Note:
If you want to retrieve the data from the Spreadsheet on your own Google Drive, please share the Spreadsheet with the email of the service account. By this, the Spreadsheet can be retrieved from the service account. Please be careful this.
In my environment, I could confirm that the modified script worked. But if this didn't resolve your issue, I apologize.

Related

Gmail API service account request- Precondition check failed

I'm trying to work with the google API's for the first time, and when I attempt to make a request to the gmail API I'm getting a "precondition check failed" error. I am using a service account authorization, not Oauth2 user consent. Things I've tried:
Authorized "domain wide delegation" for the service account.
Ensured the APP is trusted in the G suite account.
Ensured service account role is "owner"
Enabled domain wide delegation for the client ID of the service account in the g suite admin panel.
This is an adapted sample from the Node client library, but the sample did not use service account auth so I wasn't able to use the sample directly.
const path = require('path');
const {google} = require('googleapis');
const gmail = google.gmail('v1');
async function runSample() {
// Obtain user credentials to use for the request
const auth = new google.auth.GoogleAuth({
keyFile: path.resolve(__dirname, 'google-key.json'),
scopes: ['https://www.googleapis.com/auth/gmail.readonly'],
});
google.options({auth});
const res = await gmail.users.messages.list({userId: 'me'}); // have tried with my gsuite email address as well
console.log(res.data);
return res.data;
}
if (module === require.main) {
runSample().catch(console.error);
}
module.exports = runSample;
Returning error with message: Error: Precondition check failed.
After searching the dark web for eternity, I found a link to a github issue that described how to authenticate as a service using JWT auth.
This is a working version of what I was trying to accomplish:
const path = require('path');
const {google} = require('googleapis');
async getMessageList(userId, qty) {
const JWT = google.auth.JWT;
const authClient = new JWT({
keyFile: path.resolve(__dirname, 'google-key.json'),
scopes: ['https://www.googleapis.com/auth/gmail.readonly'],
subject: 'admin#example.com' // google admin email address to impersonate
});
await authClient.authorize(); // once authorized, can do whatever you want
const gmail = google.gmail({
auth: authClient,
version: 'v1'
});
const response = await gmail.users.messages.list({
includeSpamTrash: false,
maxResults: qty,
q: "",
userId: userId
});
// the data object includes a "messages" array of message data
return response.data;
}

NodeJS Googleapis Service Account authentication

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

Cant disconnect from xero using xero.disconnect

Im using AdonisJs as my Node Framework, the xeroclient config works perfectly, I can extract details that I need.
I just cant disconnect, as I saw in their sample app we just have to call xero.disconnect, yet i am receiving an error xero.disconnect is not a function
im using the xero-node npm package
const xeroNode = require('xero-node')
const xero = new xeroNode.XeroClient({
clientId: Config.get('xero.client_id'),
clientSecret: Config.get('xero.client_secret'),
redirectUris: [Config.get('xero.redirectUri')],
scopes: Config.get('xero.scopes').split(" ")
})
async disconnect ({ response, session }) {
...
await xero.disconnect(xero.tenantIds[0])
...
}
Only thing that looks incorrect is the parameter you are passing to the function. That should actually be the connection object's ID not the tenantId itself: await xero.disconnect(xero.tenants[0].id)
But other than that can you elaborate the package version you are using? I've just mimicked everything about how you are importing and setting up client, so it's unclear why the disconnect function is not available. Please ensure you are using the most recent version 4.6.0 - the following just worked for me after I successfully got my access token back from the /callback flow.
Setup:
const xeroNode = require('xero-node')
const xero = new xeroNode.XeroClient({
clientId: client_id,
clientSecret: client_secret,
redirectUris: [redirectUrl],
scopes: scopes.split(' '),
});
/connect
const consentUrl: = await xero.buildConsentUrl();
/callback
const tokenSet = await xero.apiCallback(returning_url);
await xero.updateTenants();
/disconnect
const connection = xero.tenants[0]
await xero.disconnect(connection.id)
If you can post/log some more info can get this sorted for you!

Authenticate googleapis library in Google Cloud Function

For developing locally, most google cloud client libraries are configured to use the GOOGLE_APPLICATION_CREDENTIALS environment variable to locate the credentials for the service account in use, and then authenticate that library. When deployed to GCP, they similarly don't require any manual authentication in the code, and instead use they environment to authenticate behind the scenes. This means that most client libraries, for example BigQuery, Cloud Storage, etc, just work in Cloud Functions, without needing any code for authentication. However, the googleapis Nodejs client library doesn't use GOOGLE_APPLICATION_CREDENTIALS and seems to require manual authentication in the code. Below is a minimal example of how I am doing this locally. How could I run this code in a Google Cloud Function without needing to upload the service account credentials to the cloud function?
const { google } = require("googleapis");
const key = require("service_account_credentials.json");
const client = new google.auth.JWT(key.client_email, null, key.private_key, [
"https://www.googleapis.com/auth/spreadsheets",
]);
client.authorize(function (err, tokens) {
const gsapi = google.sheets({ version: "v4", auth: client });
const opt = {
spreadsheetId: "spreadsheetId",
range: "Sheet1!A:T",
};
gsapi.spreadsheets.values.get(opt).then(res => console.log(res));
});
I managed to find a solution to this in the readme.md of the googleapis nodejs github repo. To solve my problem I used:
async function main() {
const auth = new google.auth.GoogleAuth({
scopes: ["https://www.googleapis.com/auth/spreadsheets"],
});
const authClient = await auth.getClient();
const project = await auth.getProjectId();
const gsapi = google.sheets({ version: "v4", auth: authClient });
const opt = {
spreadsheetId: "spreadsheetID",
range: "Sheet1!A:T",
};
gsapi.spreadsheets.values.get(opt).then(res => console.log(res.data.values));
}

Retrieve birthdays and genders from people API in server side with token generated client side

I would like to retrieve birthday and gender from google people API in my backend nodejs server.
The access token is generated client side with those 2 scopes:
https://www.googleapis.com/auth/user.birthday.read
https://www.googleapis.com/auth/userinfo.profile
The client sends the accessToken and the server queries the people API in the following way :
const {google} = require('googleapis');
async function getDataFromPeopleAPI(googleId, accessToken) {
try {
let params = {
resourceName: `people/${googleId}`,
personFields: 'birthdays,genders',
access_token: accessToken //generated by client
};
let res = await google.people({
auth: GOOGLE_API_KEY //API key
}).people.get(params);
let {birthdays, genders} = res.data;
} catch (e) {
}
};
The issue is that even though my birthday is set as public and my gender the people api always returns the same result . I don't receive any error code but I never receive the data I want. Here is the response I get:
"resourceName": "people/102865456870877320332",
"etag": "%EgUBBwg3LhoEAQIFBw=="
}
What am I doing wrong when querying the people API ?
Thanks !
This might be too old to answer, but here is the correct format of the request:
const {google} = require('googleapis');
let userData = await google
.people({
version: "v1", // mention the API version
auth: process.env.GOOGLE_SERVER_API_KEY,
})
.people.get({
resourceName: "people/me", // not people/${googleId}
personFields: "genders,birthdays", // mention your scopes
access_token: accessToken, // generated by client
});
Refer to this URL for scope documentation:
https://developers.google.com/people/api/rest/v1/people/get

Resources