How to get user specific data on a Google Analytics request? - node.js

I'm trying to create a web app where a user can grant access to her Google Analytics account via OAuth2. After positive response I would like to make a request to that user's GA data (in the real application the request would be made "offline"). But when calling:
google.analytics('v3').data.ga.get(params, callback);
params should contain ids, which should be a list of "table ID"s from the user. How do I get hold of these IDs? Is it necessary to get this information through another profile-scoped-request?
Code:
var google = require('googleapis');
var OAuth2 = google.auth.OAuth2;
var clientId = '123-123.apps.googleusercontent.com';
var clientSecret = 'abc';
var redirectUrl = 'http://localhost:8080/redirect';
var authRequest = function(req, res) {
var oauth2Client = new OAuth2(clientId, clientSecret, redirectUrl);
var scopes = [ 'https://www.googleapis.com/auth/analytics.readonly' ],
params = { state: 'some-data', access_type: 'offline', scope: scopes };
var url = oauth2Client.generateAuthUrl(params);
res.redirect(301, url); // will return to "authResult()"
};
var _sampleAnalytics = function(req, res, oauthClient) {
var params = {
auth: oauthClient,
metrics: 'ga:visitors,ga:visits,ga:pageviews',
'start-date': '2015-06-01',
'end-date': '2015-06-30',
ids: ['ga:123456789'] // <== How to get this parameter?
};
google.analytics('v3').data.ga.get(params, function(err, response) {
// todo
});
};
var authResult = function (req, res) {
if (req.query.error) {
return handleError(res, req.query.error);
}
var oauth2Client = new OAuth2(clientId, clientSecret, redirectUrl);
oauth2Client.getToken(code, function(err, tokens) {
// Now tokens contains an access_token and an optional refresh_token. Save them.
if(err) {
return handleError(res, err);
} else {
oauth2Client.setCredentials(tokens);
_sampleAnalytics(req, res, oauth2Client);
}
});
};

Ok, that was simple. I just need to make another call to:
google.analytics('v3').management.accountSummaries.list(params, function(err, result) {
// ...
});
result will contain all the required information.

Related

passport-apple Node.js login error - Failed to obtain access token

I am a junior engineer working in a start-up in Seoul, Korea.
In the current project, I am trying to use the passport module to develop apple login.
I have already finished developing google social login, but I faced some problems while trying to do the same with apple.
The problem is that I get an error that states : "Failed to obtain access token".
I got really confused that you have to use the POST method in order to get the profile info from apple.
Can someone please help me?? Thanks in advance!
IT would be wonderful if I could success. Thanks again
The main problem I am expecting is that
passport.authenticate('apple') calls the function which handles passport module for google that I have already developed.
I send the redirect url to Frontend, in order to open the browser in my application.
ROUTER.get('/apple/login', async function (req, res) {
const result = { status: 'N' };
const config = {
client_id: APPLE_AUTH.CLIENT_ID, // This is the service ID we created.
redirect_uri: APPLE_AUTH.REDIRECT_URI, // As registered along with our service ID
response_type: 'code id_token',
// state: 'origin:web', // Any string of your choice that you may use for some logic. It's optional and you may omit it.
scope: 'name email', // To tell apple we want the user name and emails fields in the response it sends us.
response_mode: 'form_post',
m: 11,
v: '1.5.4',
};
const queryString = Object.entries(config)
.map(([key, value]) => `${key}=${encodeURIComponent(value)}`)
.join('&');
const redirectUrl = `https://appleid.apple.com/auth/authorize?${queryString}`;
result['redirectUrl'] = redirectUrl;
result['status'] = 'Y';
res.json(result);
});
I get the url, and open the browser
async openAppleSignIn() {
const result = await this.authService.postAppleLogin();
// await this.authService.postAppleLogin();
if (result.data.status === 'Y') {
const { redirectUrl } = result.data;
console.log(redirectUrl);
if (this.deviceInfo.platform !== 'web') {
// this.browserService.openBrowser(redirectUrl);
window.open(redirectUrl, '_self');
console.log('enter');
} else {
const timer = setInterval(async () => {
if (this.newWindowForLogin.closed) {
clearInterval(timer);
}
}, 500);
const isChrome = this.commonService.checkBrowserIsChrome();
if (!this.newWindowForLogin || isChrome) {
// this.newWindowForLogin = window.open();
window.open(redirectUrl);
}
// this.newWindowForLogin.location.href = redirectUrl;
}
} else {
}
Apple strategy
const passport = require('passport');
// var AppleStrategy = require('passport-google-oauth20').Strategy;
var AppleStrategy = require('passport-apple').Strategy;
const jwt = require('jsonwebtoken');
// var fs = require('fs');
const APPLE_AUTH = require('../../../config/secret_key').get('APPLE_AUTH');
const UserModelService = require('../../api/user/model/user_model_service');
// const applePrivateKey = fs.readFileSync('config/AuthKey_Y8BG5JY7P3.p8');
// console.log(applePrivateKey);
module.exports = () => {
passport.use(
'apple',
new AppleStrategy(
{
clientID: APPLE_AUTH.CLIENT_ID,
teamID: APPLE_AUTH.TEAM_ID,
callbackURL: APPLE_AUTH.LOGIN_CALLBACK_URL,
keyID: APPLE_AUTH.KEY_ID,
privateKeyLocation: 'config/AuthKey_Y8BG5JY7P3.p8',
privateKeyString: APPLE_AUTH.privateKey,
passReqToCallback: true,
},
async (accessToken, refreshToken, idToken, profile, done) => {
try {
// console.log(profile._json.email);
// const socialLoginEmail = req.user.emails[0].value;
// console.log(email);
//화면에서 백으로
// console.log(profile._json.email);
// const user = await UserModelService.getUser(profile._json.email);
// console.log(user);
console.log('jwt', jwt.decode(idToken));
console.log('strategy', req);
done(null, idToken);
} catch (error) {
console.error(error);
// done(error);
}
},
),
);
};
index.js
const passport = require('passport');
const apple = require('./apple_auth');
const google = require('./google_auth');
module.exports = () => {
apple();
google();
};
5.Then, I intended to get the results in callback
ROUTER.post(
'/apple/callback',
passport.authenticate('apple', { failureRedirect: '/', session: false }),
async (req, res) => {
try {
console.log(res);
console.log(req);
} catch (err) {}
},
);
I customized the passport-apple usage into this way, not following the instructions in the passport docs, because the way they listed in the official document did not work for my code.
Thanks again, and I hope to find an answer in stack overflow!!

Verify email content using mocha

I am using Node JS, mocha and googleapi writing tests to verify email content
When i run the googleapi as a standalone node js file i am able to get mails but when i integrate it with mocha tests i am not seeing any result, please help
test spec file (verify.js)
var checkEmail = require('../shared/checkEmail');
const { google } = require('googleapis');
var expect = require('chai').expect;
var chai = require('chai');
var chaiAsPromised = require('chai-as-promised');
const { isNullOrUndefined } = require('util');
chai.use(chaiAsPromised);
chai.should();
var emailRecords = new Array();
var content;
var auth;
describe('GMAIL Testing', function () {
before('Connect to Gmail server', function () {
content = checkEmail.getAuthentication();
auth = checkEmail.authorize(content);
// Random test data
var a = [{ "Id": "123", "MsgId": "34677", "Type": "aaa", "Subject": "subxxxx", "ToAddress": "abc#gmail.com", "ToName": "ABC", "DateCreated": "2020-07-09T18:25:38.047Z" }];
emailRecords.push(a);
var b = [{ "Id": "456", "MsgId": "34655", "Type": "bbb", "Subject": "subject", "ToAddress": "abc#gmail.com", "ToName": "ABC", "DateCreated": "2020-06-09T18:25:38.047Z" }];
emailRecords.push(b);
});
it('Gmail Verification', function () {
emailRecords.forEach(element => {
const gmail = google.gmail({ version: 'v1', auth });
var query = "from:noreply#somedomain.com " + element[0].MsgId;
console.log('getting mail '+ element[0].MsgId);
gmail.users.messages.list({
userId: 'me',
q: query
}, (err, res) => {
if (err) return console.log('The API returned an error: ' + err);
var mails = res.data.messages;
console.log('mail(s) found');
expect(mails.length).to.be.at.least(1);
});
console.log('completed search');
});
});
});
Utility File checkEmail.js Ref -> Gmail API
const fs = require('fs');
const readline = require('readline');
const { google } = require('googleapis');
var base64 = require('js-base64').Base64;
const cheerio = require('cheerio');
var open = require('open');
const { isNullOrUndefined } = require('util');
var Mailparser = require('mailparser').MailParser;
const SCOPES = ['https://www.googleapis.com/auth/gmail.readonly'];
// The file token.json stores the user's access and refresh tokens, and is
// created automatically when the authorization flow completes for the first
// time.
const TOKEN_PATH = 'token.json';
module.exports = new class checkEmail {
getAuthentication() {
// Load client secrets from a local file.
console.log('getting auth');
this.content = JSON.parse(fs.readFileSync('shared/config/credentials.json', 'utf8'));
return this.content;
}
authorize(credentials) {
const { client_secret, client_id, redirect_uris } = credentials.installed;
const oAuth2Client = new google.auth.OAuth2(
client_id, client_secret, redirect_uris[0]);
var token = fs.readFileSync(TOKEN_PATH, 'utf-8');
if (token == isNullOrUndefined) {
token = getNewToken(oAuth2Client);
}
oAuth2Client.setCredentials(JSON.parse(token));
return oAuth2Client;
}
getNewToken(oAuth2Client) {
const authUrl = oAuth2Client.generateAuthUrl({
access_type: 'offline',
scope: SCOPES,
});
var newToken;
console.log('Authorize this app by visiting this url:', authUrl);
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
rl.question('Enter the code from that page here: ', (code) => {
rl.close();
oAuth2Client.getToken(code, (err, token) => {
if (err) return console.error('Error retrieving access token', err);
oAuth2Client.setCredentials(token);
// Store the token to disk for later program executions
fs.writeFile(TOKEN_PATH, JSON.stringify(token), (err) => {
if (err) return console.error(err);
console.log('Token stored to', TOKEN_PATH);
});
newToken = token;
});
});
return newToken;
}
}
I have tried adding debug messages but nothing is printed nor any error is thrown, Please let me know if i am missing something
output after running tests
> .\node_modules.bin\mocha .\test\verify.js
Tests are appearing to be passed but console.log('mail(s) found');
didnt show up in the output
Your test is finishing before your network request finishes.
See this section in the mocha docs.
https://mochajs.org/#asynchronous-code
You need to either use the done callback or return a promise.
If you can use async/await I find this to be the easiest because an async function always returns a promise: https://mochajs.org/#using-async-await

Issue with Google oAuth2 callback using Firebase functions

I would like use Firebase Functions to use the Google Developer API. Authentification is required to use this API.
I follow the doc: https://github.com/googleapis/google-api-nodejs-client
I have some troubles to get the authorization code in the callback url.
var {google} = require('googleapis');
google.options({ auth: oauth2Client });
var oauth2Client = new google.auth.OAuth2(
'XXXX.apps.googleusercontent.com',
'XXXX',
'https://us-central1-XXXX.cloudfunctions.net/oauth2callback'
);
function generateAuthenticationUrl() {
return oauth2Client.generateAuthUrl({
access_type: 'offline',
prompt: 'consent',
scope: 'https://www.googleapis.com/auth/androidpublisher'
});
}
exports.oauth2Callback = functions.https.onRequest((req, res) => {
console.log(req.query.code);
const code = req.query.code;
//do something
return null;
});
exports.hello = functions.https.onRequest((req, res) => {
var url = generateAuthenticationUrl();
console.log(url);
//-> url print in the console is : https://accounts.google.com/o/oauth2/v2/auth?access_type=offline&prompt=consent&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fandroidpublisher&response_type=code&client_id=XXXXX-XXX.apps.googleusercontent.com&redirect_uri=https%3A%2F%2Fus-central1-XXX.cloudfunctions.net%2Foauth2callback
res.redirect(url);
});
Redirect url is set in the Google Console Developer:
When I call the url https://us-central1-XXX.cloudfunctions.net/hello, I got "Error: could not handle the request" and "finished with status: 'timeout'" in the Firebase logs.
What's wrong?
I found a solution.
Full code using JWT to authenticate, then get the list of app's reviews:
const functions = require('firebase-functions')
const admin = require('firebase-admin')
admin.initializeApp(functions.config().firebase);
var {google} = require('googleapis');
const serviceAccount = require('./client_secret.json');
const { JWT } = require('google-auth-library');
const getAuthorizedClient = () => new JWT({
email: serviceAccount.client_email,
key: serviceAccount.private_key,
scopes: ['https://www.googleapis.com/auth/androidpublisher']
});
const getAndroidpublisher = () => google.androidpublisher({
version: 'v3',
auth: getAuthorizedClient()
});
const requestProductValidation = () => new Promise((resolve, reject) => {
getAndroidpublisher().reviews.list({
packageName: "com.my.packagename"
}, (err, response) => {
if (err) {
console.log(`The API returned an error: ${err}`);
resolve({status: "Error"});
} else {
return resolve(response);
}
});
});
exports.hello = functions.https.onRequest((req, res) => {
return requestProductValidation();
});

UnhandledPromiseRejectionWarning: ReferenceError: message is not defined Discord.js

I'm in the process of creating a Discord bot that will read from a specific Google Sheets spreadsheet and this error keeps coming up as I am trying to integrate the Google Sheets functions. See Github Repo and please know I'm extremely new at Node.js.
For this function, I have created index.js to get everything running for both discord.js and the Google API:
require("dotenv").config();
const fs = require("fs");
const Discord = require("discord.js");
const client = new Discord.Client();
const readline = require('readline');
const { google } = require('googleapis');
const OAuth2Client = google.auth.OAuth2;
const SCOPES = ['https://www.googleapis.com/auth/spreadsheets'];
const TOKEN_PATH = 'token.json';
/**
* Create an OAuth2 client with the given credentials, and then execute the
* given callback function.
* #param {Object} credentials The authorization client credentials.
* #param {function} callback The callback to call with the authorized client.
*/
const authorize = function (credentials, callback) {
const { client_secret, client_id, redirect_uris } = credentials.installed;
const oAuth2Client = new OAuth2Client(client_id, client_secret, redirect_uris[0]);
// Check if we have previously stored a token.
fs.readFile(TOKEN_PATH, (err, token) => {
if (err) return getNewToken(oAuth2Client, callback);
oAuth2Client.setCredentials(JSON.parse(token));
callback(oAuth2Client);
});
}
/**
* Get and store new token after prompting for user authorization, and then
* execute the given callback with the authorized OAuth2 client.
* #param {google.auth.OAuth2} oAuth2Client The OAuth2 client to get token for.
* #param {getEventsCallback} callback The callback for the authorized client.
*/
const getNewToken = function (oAuth2Client, callback) {
const authUrl = oAuth2Client.generateAuthUrl({
access_type: 'offline',
scope: SCOPES,
});
console.log('Authorize this app by visiting this url:', authUrl);
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
rl.question('Enter the code from that page here: ', (code) => {
rl.close();
oAuth2Client.getToken(code, (err, token) => {
if (err) return callback(err);
oAuth2Client.setCredentials(token);
// Store the token to disk for later program executions
fs.writeFile(TOKEN_PATH, JSON.stringify(token), (err) => {
if (err) console.error(err);
console.log('Token stored to', TOKEN_PATH);
});
callback(oAuth2Client);
});
});
}
//pulls all commands and events
fs.readdir("./events/", (err, files) => {
files.forEach(file => {
const eventHandler = require(`./events/${file}`);
const eventName = file.split(".")[0];
client.on(eventName, (...args) => eventHandler(client, ...args));
});
});
client.login(process.env.BOT_TOKEN);
module.exports = {
authorize,
google
}
Sheets.js contains the readAll function for Sheets I want to use so when someone types in assignments!all the function should run:
const {authorize, google} = require('./index');
const fs = require("fs");
const readline = require('readline');
const Discord = require("discord.js");
const client = new Discord.Client();
const spreadsheetId = "1asvhCVI1sC6Q2cCuqdUrRqFHkH2VCr5FM7kWSm8VBE8";
//read entire spreadsheet
const readAll = (range) => {
fs.readFile('client_secret.json', (err, content) => {
if (err) return console.log('Error loading client secret file:', err);
// Authorize a client with credentials, then call the Google Sheets API.
authorize(JSON.parse(content), (auth) => {
const sheets = google.sheets({ version: 'v4', auth });
sheets.spreadsheets.values.get({
spreadsheetId, range
}, (err, res) => {
if (err) return console.log('The API returned an error:' + err);
const rows = res.data.values;
if (rows.length) {
// Print columns B through F.
rows.map((row) => {
message.reply(`Details: ${row[0]}, Link: ${row[1]}, Additional Link: ${row[2]}, Assigned To: ${row[3]}, Assigned On: ${row[4]}, Ready for QC on: ${row[5]}, QC Link: ${row[6]}`);
});
} else {
console.log('No data found.');
}
});
});
});
}
module.exports = {
readAll
};
As I plan to have the bot do more functions in the future, I have created a message.js file in an events folder to call commands from a commands folder:
const assignments = require('../commands/assignments');
const assign = require('../commands/assign');
const qc = require('../commands/qc');
const qcList = require('../commands/qcList');
const sheets = require('../sheets');
module.exports = (client, message) => {
if (message.content.startsWith('assignments!')) {
return assignments(message);
} else if (message.content.startsWith('assign!')) {
return assign(message);
} else if (message.content.startsWith('qc!')) {
return qc(message);
} else if (message.content.startsWith('qclist!')) {
return qcList(message);
}
};
Assignments.js actually tells the bot that when the word "all" is used after the ! in "assignments!", that is when it is suppose to run the ReadAll function on the specific tab in the Sheets spreadsheet:
// gets all assignments or assignments based on name given after !
module.exports = (message, google, authorize, readAll) => {
const sheets = require('../sheets');
//ex message: assignments!Brody
//gets name after ! and stores as member variable
var messageContent = message.content;
var member = messageContent.split('!')[1];
var member = member.toLowerCase();
//test reading spreadsheet
if(member === 'all') {
sheets.readAll("Active News Assignments!B2:F");
}
//error messages
if (!member) {
message.reply("You must add a name or 'all' after the command.");
}
};
Again, I am very new to Node.js and following these tutorials to create this bot. Please feel free point out anything obvious I'm missing or ways I can simplify my code once it's working as I know I will be adding more functions in the future.
https://levelup.gitconnected.com/making-a-custom-discord-bot-in-discord-js-1e17f2090919
https://www.ishaanrawat.com/integrate-google-sheets-api-with-node-js/

Google Drive API Service Account inside domain

I have to download/upload/delete files from a folder on Drive with a Node.js server. That folder is inside the G Suite of the company, and only a few people in the company has access.
I have to use a Service Account to do this, the question is: is it possible? How can I do that?
I already read https://developers.google.com/drive/v2/web/delegation and https://developers.google.com/identity/protocols/OAuth2ServiceAccount
but I don't know if it is possible give permissions to a service account to access a folder inside the domain of the company, because the service account is #developer.gserviceaccount.com and the domain of the company is other, so gives me an error when I try to add that service account to the folder.
If you could guide me on this, I'll be very greatful.
Thanks!
You can use an oAuth token with the rights scope(s):
const path = require('path');
module.exports = (app) => {
const factory = {};
factory.connect = (done) => {
const fs = require('fs');
const google = require('googleapis');
const googleAuth = require('google-auth-library');
const SCOPES = [
'https://www.googleapis.com/auth/drive.metadata.readonly'
];
const TOKEN_DIR = path.resolve(app.root, 'server','config');
const TOKEN_PATH = path.resolve(TOKEN_DIR,'token.json');
const creds = require(path.resolve(app.root, 'server', 'config', 'google_oauth.json'));
authorize(creds, (ret) => {
done(null, ret);
});
/**
* Create an OAuth2 client with the given credentials, and then execute the
* given callback function.
*
* #param {Object} credentials The authorization client credentials.
* #param {function} callback The callback to call with the authorized client.
*/
function authorize(credentials, callback) {
const clientSecret = credentials.installed.client_secret;
const clientId = credentials.installed.client_id;
const redirectUrl = credentials.installed.redirect_uris[0];
const auth = new googleAuth();
const oauth2Client = new auth.OAuth2(clientId, clientSecret, redirectUrl);
// Check if we have previously stored a token.
fs.readFile(TOKEN_PATH, function (err, token) {
if (err) {
console.error('[ERROR] Unable to read token', err)
getNewToken(oauth2Client, callback);
} else {
oauth2Client.credentials = JSON.parse(token);
callback(oauth2Client);
}
});
}
/**
* Get and store new token after prompting for user authorization, and then
* execute the given callback with the authorized OAuth2 client.
*
* #param {google.auth.OAuth2} oauth2Client The OAuth2 client to get token for.
* #param {getEventsCallback} callback The callback to call with the authorized
* client.
*/
function getNewToken(oauth2Client, callback) {
const authUrl = oauth2Client.generateAuthUrl({
access_type: 'offline',
scope: SCOPES
});
console.log('Authorize this app by visiting this url: ', authUrl);
const readline = require('readline');
const 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);
});
});
}
/**
* Store token to disk be used in later program executions.
*
* #param {Object} token The token to store to disk.
*/
function storeToken(token) {
try {
fs.mkdirSync(TOKEN_DIR);
} catch (err) {
if (err.code != 'EEXIST') {
throw err;
}
}
fs.writeFile(TOKEN_PATH, JSON.stringify(token));
console.log('Token stored to ' + TOKEN_PATH);
}
};
return factory
};
and then factory.connect(done) will give done an auth to use googleapis:
const google = require('googleapis');
const service = google.drive('v3');
service.files.list({
auth,
pageSize: 10,
fields: 'nextPageToken, files(id, name)'
}, step);

Resources