Cant disconnect from xero using xero.disconnect - node.js

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!

Related

YouTube Data v3 API - How to request all videos from a channel?

I am writing a Node.js script which will run in Lambda to periodically request the list of every video (public, unlisted, or private) for one of my organization's channels via the YouTube Data v3 API. In order to do this, it appears there are two steps:
Executing the channels/list call https://developers.google.com/youtube/v3/docs/channels/list to get the "Uploads" playlist.
const channelResult = await google.youtube('v3').channels.list({
auth: youtubeApiKey,
part: ['snippet', 'contentDetails'],
id: ["my_channel_id"]
});
Executing the playlistItems/list https://developers.google.com/youtube/v3/docs/playlistItems/list to see all the videos.
const videosResult = await google.youtube('v3').playlistItems.list({
auth: youtubeApiKey,
part: ['snippet', 'status'],
playlistId: "my_uploads_playlsit_id"
});
This only ever executes as a script running the cloud; there is no user interface or browser component. This all appeared to work in my test channel when I set the lone video there to public. But if I set it to private, I get:
The playlist identified with the request's <code>playlistId</code> parameter cannot be found.
What do I have to do in order to still access the Uploads playlist of my channel, and show private videos? Thanks!
With help from #stvar in the original question's comments, I was able to achieve this. The flow is as such:
Enable the YouTube Data API v3 from the Google Developers Console via the Enable APIs and Services.
Create a new OAuth client ID under YouTube Data API v3's "Credentials" pane and select Desktop app.
Save the client_id and client_secret. Make these accessible to your Node app via whatever environment variable method you prefer.
Create a separate script specifically for getting a refresh_token via YouTube Data API v3 OAuth
import { google } from 'googleapis';
import prompts from 'prompts';
console.log("about to execute oauth");
const yt_client_id = process.env.YOUTUBE_CLIENT_ID;
const yt_client_secret = process.env.YOUTUBE_CLIENT_SECRET;
const oauthClient = new google.auth.OAuth2({
clientId: yt_client_id,
clientSecret: yt_client_secret,
redirectUri: 'http://localhost'
});
const authUrl = oauthClient.generateAuthUrl({
access_type: 'offline', //gives you the refresh_token
scope: 'https://www.googleapis.com/auth/youtube.readonly'
});
const codeUrl = await prompts({
type: 'text',
name: 'codeURl',
message: `Please go to \n\n${authUrl}\n\nand paste in resulting localhost uri`
});
const decodedUrl = decodeURIComponent(codeUrl.codeURl);
const code = decodedUrl.split('?code=')[1].split("&scope=")[0];
const token = (await oauthClient.getToken(code)).tokens;
const yt_refresh_token = token.refresh_token;
console.log(`Please save this value into the YOUTUBE_REFRESH_TOKEN env variable for future runs: ${yt_refresh_token}`);
await prompts({
type: 'text',
name: 'blank',
message: 'Hit enter to exit:'
});
process.exit(0);
Save the refresh token in another environment variable, accessible to your main data-fetching script. Use it as such:
import { google } from 'googleapis';
console.log("Beginning youtubeIndexer. Checking for valid oauth.");
const yt_refresh_token = process.env.YOUTUBE_REFRESH_TOKEN;
const yt_client_id = process.env.YOUTUBE_CLIENT_ID;
const yt_client_secret = process.env.YOUTUBE_CLIENT_SECRET;
const yt_channel_id = process.env.YOUTUBE_CHANNEL_ID;
const oauthClient = new google.auth.OAuth2({
clientId: yt_client_id,
clientSecret: yt_client_secret,
redirectUri: 'http://localhost'
});
oauthClient.setCredentials({
refresh_token: yt_refresh_token
});
const youtube = google.youtube("v3");
const channelResult = await youtube.channels.list({
auth: oauthClient,
part: ['snippet', 'contentDetails'],
id: [yt_channel_id]
});
let nextPageToken = undefined;
let videosFetched = 0;
do {
const videosResult = await youtube.playlistItems.list({
auth: oauthClient,
maxResults: 50,
pageToken: nextPageToken,
part: ['snippet', 'status'],
playlistId: channelResult.data.items[0].contentDetails.relatedPlaylists.uploads
});
videosFetched += videosResult.data.items.length;
nextPageToken = videosResult.data.nextPageToken;
videosResult.data.items.map((video, index) => {
//process the files as you need to.
});
} while (nextPageToken);
This last .map() function, marked with the "process the files as you need to" comment will receive every video in the channel, whether it be public, unlisted, or private.
NOTE: I do not know yet how long a given refresh_token will last, but assume that you will regularly need to run the first script again and update the refresh_token used via the second script's environment variable.

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

Google Sheets Api Authorization with a service account

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.

How can I use the Google OAuth2 ID token with node.js / passport.js to verify if the user is valid and authenticated?

On my front end, I'm using vue.js (not that that matters) and with the Google OAuth flow, I get back an id_token from:
let googleAuthIdToken = await this.auth2.currentUser
.get()
.getAuthResponse().id_token;
I want to then pass that token to my node.js server (Express / Passport) to verify that the user is allowed to login.
I want to use passport and send back a JWT to the front end in my response.
Can someone please guide me as to how to accomplish this?
It is easier to make use of a node module called googleapis, After installation, import the module.
import { google } from 'googleapis';
Then you need to create an OAuthClient by specifying the CLIENT_ID, CLIENT_SECRET, REDIRECT_URL.
const oauth2Client = new google.auth.OAuth2(
CLIENT_ID,
CLIENT_SECRET,
REDIRECT_URL,
);
Then you can get the token from google by using the oauth2Client.
const {tokens} = await oauth2Client.getToken(code);
oauth2Client.setCredentials(tokens);
Inorder to obtain the neccessary user information to store in your own database, You need to call this method.
const plus = google.plus({ version: 'v1', oauth2Client });
const me = await plus.people.get({ userId: 'me' });
me will contain the user information that you are looking for, once you obtain the user information you can then store it using passport js.

Resources