Using domain-level delegation with Google Classroom API - node.js

I'm trying to access teacher's class info using a node script. I can do it if I authenticate as the teacher themselves but I need a server connection to build a database. I'm not sure if this is a google classroom problem or if this is a bug on my end. I could be just thinking about authentication in the wrong way but I'm not sure. I am using this to authenticate:
module.exports.auth = function auth(type, callback) {
switch(type){
case "jwt":
useJwt(process.env.LXMUSER, callback)
break;
case "default":
useDefaultCred(callback);
break;
case "user":
useUser(callback);
break;
}
}
function useJwt(user, callback){
var google = require('googleapis');
var cfg = require('./config.json');
var key = require(process.env.GOOGLE_APPLICATION_CREDENTIALS);
var jwtClient = new google.auth.JWT(
key.client_email,
null,
key.private_key,
cfg.scopes,
user
);
jwtClient.authorize(function (err, tokens) {
if (err) {
console.error('Unable to authenticate with google:', err);
return
}
callback(err, jwtClient)
})
}
I added these scopes to the Oauth client manager:
classroom.courses,
classroom.coursework.students,
drive.readonly,
drive.file,
drive.metadata.readonly,
drive.appdata
I can successfully use the API call through the google drive API but not the google classroom api. Here is my calling environment:
auth("jwt", function (err, authClient) {
// Make an authorized request to list Drive files.
gclass.courses.list({
auth: authClient,
}, function (err, res) {
if (err) {
utl.errorHandle(res, err);
return;
}
if (res.courses === undefined) {
console.log(res, "You have no courses");
}

Related

How to request data from google analytics multi-channel funnels

I can successfully request data from the google analytics core reporting api but am struggling to pull from the multi-channel funnels api. I am pretty sure my problem is with permission/access but I've gone through the documentation and can't figure out exactly what the problem is. I am using node and the googleapis library. Here is my code for pulling the google analytics information (This works)
var {google} = require('googleapis');
var key = require('./auth.json');
var viewID = 'XXXXXXX';
var analytics = google.analyticsreporting('v4');
var jwtClient = new google.auth.JWT(key.client_email,
null,
key.private_key,
['https://www.googleapis.com/auth/analytics.readonly'],
null);
jwtClient.authorize(function (err, tokens) {
if (err) {
console.log(err);
return;
} else {
console.log('Yup, we got authorized!');
console.log(tokens)
}
});
var req = {
reportRequests: [{
viewId: viewID,
dateRanges: [{
startDate: '2020-11-01',
endDate: '2020-11-01'}],
metrics: [{expression: 'ga:pageViews'}],
dimensions: [{name: 'ga:city'}],
pageSize: 10
}],
};
analytics.reports.batchGet({
auth: jwtClient,
resource: req
},
function (err, response) {
if (err) {
console.log('Failed to get Report');
console.log(err);
return;
}
console.log('Success');
console.log(response.data)
}
);
This produces the desired output just fine. I tried to change the metrics and dimensions so I can pull some multi-channel funnel information that I want but am getting an error
var {google} = require('googleapis');
var key = require('./auth.json');
var viewID = 'XXXXXXX';
var analytics = google.analyticsreporting('v4');
var jwtClient = new google.auth.JWT(key.client_email,
null,
key.private_key,
['https://www.googleapis.com/auth/analytics.readonly'],
null);
jwtClient.authorize(function (err, tokens) {
if (err) {
console.log(err);
return;
} else {
console.log('Yup, we got authorized!');
console.log(tokens)
}
});
var req = {
reportRequests: [{
viewId: viewID,
dateRanges: [{
startDate: '2020-11-01',
endDate: '2020-11-01'}],
metrics: [{expression: 'mcf:sourceMedium'}],
dimensions: [{name: 'mcf:lastInteractionConversions'}],
pageSize: 10
}],
};
analytics.reports.batchGet({
auth: jwtClient,
resource: req
},
function (err, response) {
if (err) {
console.log('Failed to get Report');
console.log(err);
return;
}
console.log('Success - got something back from the Googlez');
console.log(response.data)
}
);
It is producing this
code: 400,
errors: [
{
message: 'Unknown dimensions(s): mcf:lastInteractionConversions, unknown metric(s): mcf:sourceMedium\n' +
'For details see https://developers.google.com/analytics/devguides/reporting/core/dimsmets.',
domain: 'global',
reason: 'badRequest'
}
]
I am pretty sure my problem is either I am requesting from the wrong source or my permissions are only available for ga and not mcf, but I don't fully understand the reason for the error or how to fix it, and the documentation hasn't provided much help. Any help would be appreciated, thanks.
You are using Reporting API V4 instead of Multi-Channel Funnels API V3: https://developers.google.com/analytics/devguides/reporting/mcf/v3/reference
The API you need provides a single method to request data:
analytics.data.mcf.get()

Can't authenticate google service account using Node.js

My goal is to allow people to add events to a calendar through my website (which uses Firebase currently.) I'm attempting to allow them to add the events to a service account; however, I'm having issues getting the service account authenticated. When I attempt to deploy my code, I get an 'unexpected token google' error at the await google.auth.getClient part. I have google API tools installed globally.
//ADD CALENDAR EVENT
const {google} = require('googleapis');
const calendar = google.calendar('v3');
//Authenticate Service Acounnt
function getCredentials() {
const filePath = path.join(__dirname, 'credentials.json';
if (fs.existsSync(filePath)) {
return require(filePath)
}
if (process.env.CREDENTIALS) {
return JSON.parse(process.env.CREDENTIALS)
}
throw new Error('Unable to load credentials')
}
//Create Calendar Event
function addEvent(event) {
return new Promise(function(resolve, reject) {
calendar.events.insert({
calendarId: 'primary',
resource: {
'summary': event.eventName,
'description': event.description,
'start': {
'dateTime': event.startTime,
'timeZone': TIME_ZONE,
},
'end': {
'dateTime': event.endTime,
'timeZone': TIME_ZONE,
},
},
}, (err, res) => {
if (err) {
console.log('Rejecting because of error');
reject(err);
}
console.log('Request successful');
resolve(res.data);
});
});
}
//Add Event To Service Acount
exports.addEventToCalendar = functions.https.onRequest((request, response) => {
const eventData = {
eventName: request.body.eventName,
description: request.body.description,
startTime: request.body.startTime,
endTime: request.body.endTime
};
const credentials = getCredentials();
const client = await google.auth.getClient({
credentials,
scopes: 'https://www.googleapis.com/auth/calendar',
})
addEvent(eventData, client).then(data => {
response.status(200).send(data);
return;
}).catch(err => {
console.error('Error adding event: ' + err.message);
response.status(500).send(ERROR_RESPONSE);
return;
});
});
I've been following a combination of these two tutorials:
https://medium.com/zero-equals-false/integrating-firebase-cloud-functions-with-google-calendar-api-9a5ac042e869
https://dev.to/mornir/create-a-service-account-to-authenticate-with-google-5b1k
I've spent a lot of time googling what could be wrong, but to be honest, this google authentication stuff confuses the heck out of me. I'd appreciate whatever help I can get, thanks :)
You want to create new event to the Google Calendar using the service account.
credentials.json is the credential file of the service account.
You want to achieve this using googleapis with Node.js.
You have already been able to use Calendar API.
If my understanding is correct, how about this modification? Please think of this as just one of several possible answers.
Modification points:
When the service account is used, you can use google.auth.JWT instead of google.auth.getClient.
In your script, client is given to addEvent(eventData, client). But at function addEvent(event) {}, client is not used.
About path.join(__dirname, 'credentials.json';, ) is required to be added. And I think that const path = require("path"); is also required to added.
Modified script:
//ADD CALENDAR EVENT
const { google } = require("googleapis");
const path = require("path"); // Added
// --- I added below script.
const key = require(path.join(__dirname, 'credentials.json'));
const jwtClient = new google.auth.JWT(
key.client_email,
null,
key.private_key,
["https://www.googleapis.com/auth/calendar"],
null
);
const calendar = google.calendar({ version: "v3", auth: jwtClient });
// ---
//Create Calendar Event
function addEvent(event) {
return new Promise(function(resolve, reject) {
calendar.events.insert(
{
calendarId: "primary",
resource: {
summary: event.eventName,
description: event.description,
start: {
dateTime: event.startTime,
timeZone: TIME_ZONE
},
end: {
dateTime: event.endTime,
timeZone: TIME_ZONE
}
}
},
(err, res) => {
if (err) {
console.log("Rejecting because of error");
reject(err);
return;
}
console.log("Request successful");
resolve(res.data);
}
);
});
}
//Add Event To Service Acount
exports.addEventToCalendar = functions.https.onRequest((request, response) => {
const eventData = {
eventName: request.body.eventName,
description: request.body.description,
startTime: request.body.startTime,
endTime: request.body.endTime
};
addEvent(eventData) // Modified
.then(data => {
response.status(200).send(data);
return;
})
.catch(err => {
console.error("Error adding event: " + err.message);
response.status(500).send(ERROR_RESPONSE);
return;
});
});
Note:
This modified script supposes that the object of request.body can be used for resource of calendar.events.insert().
Is TIME_ZONE declared elsewhere? Please be careful this.
When the email of service account is not shared with the Google Spreadsheet, the event cannot be created. So please be careful this.
References:
google-api-nodejs-client
Events: insert
If I misunderstood your situation and this was not the direction you want, I apologize.

How to get result of validateFunc in pre of auto created API Rest Hapi

I am new to Hapi.js.I am using "hapi-auth-jwt2" module for authentication token and role verification. I set the scope and sent that scope from the callback of validateFunc . It will worked very well for checking te role based authentication. But i want the result i am returning from the validateFunc but don't know where i can get that.
validateFunc: function (token, request, callback) {
Async.auto({
session: function (done) {
Session.findByCredentials(token.sessionId, token.sessionKey, done);
},
user: ['session', function (results, done) {
if (!results.session) {
return done();
}
User.findById(results.session.user, done);
}],
}, (err, results) => {
if (err) {
return callback(err);
}
if (!results.session) {
return callback(null, false);
}
results.scope = token.scope;
callback(null, Boolean(results.user), results);
});
}
});
};
`
It verify the scope or Role in the domain i.e:-
routeOptions: {
scope:{
createScope:"admin"
},
create: {
pre : function(payload, Log){
console.log("preee runnnig........");
console.log(payload);
}
}
I am getting the payload Json what i am sending from the client side but i want the results i am sending from the callback of validateFunc, because i want to use that data here in pre prior to send the request.I am working on implicitly created API via Rest Hapi Module.
So how can i get that datain pre hooks from the validateFunc . Any help is much appreciated.
Thanks
This is actually a feature that is being worked on and hopefully will be done soon.
For now, you can omit the generated create endpoint and replace it with your own in order to access the request object.
The resulting code would look something like this:
'use strict';
const RestHapi = require('rest-hapi');
module.exports = function (server, mongoose, logger) {
server.route({
method: 'POST',
path: '/pathName',
config: {
handler: function(request, reply) {
/* modify request.payload here and access auth info through request.auth.credentials */
const Model = mongoose.model('modelName');
return RestHapi.create(Model, request.payload, logger)
.then(function (result) {
return reply(result);
})
.catch(function (error) {
return reply(error);
});
},
tags: ['api'],
plugins: {
'hapi-swagger': {}
}
}
});
};

I need to connect my existing chrome extension to firebase .could anyone pls suggest a new way?

I've an existing application which uses:
$scope.signInGoogle = function(){
var ref = new Firebase(URL);
chrome.identity.getAuthToken({
interactive: true
}, function(token) {
localStorage.setItem("token",token);
if (chrome.runtime.lastError) {
alert(chrome.runtime.lastError.message);
return;
}
// Authenticate with Google using an existing OAuth 2.0 access token
ref.authWithOAuthToken("google", token, function(error, authData) {
if (error) {
console.log("Login Failed!", error);
} else {
console.log("Authenticated successfully with payload:", authData);
$state.go("app.profile");
}
});
});};
Need to know how to use Identity API to login to firebase

How to write a setup for an NPM package

I am writing my first node.js package to help make a REST API easier to use and I am having trouble formatting the package to allow the person using the package to do the below in their applications.
var Water= require("water-reservation");
var water = Water({username: myusername, password: mypassword});
// Problem here because Water({}) needs to do a REST call to get a bearer
// token to use the API I am trying to simplify. Thus, it tries to
// get_water(callabck) before the bearer_token var is set in my package.
water.get_water(function(err, result){
if(err){
console.log(err);
console.log("----------------------------------------------");
}
else{
console.log(result);
console.log("----------------------------------------------");
}
});
In my current setup I take the user's username and password and pass it to a REST endpoint to get a bearer token that I use in all my REST calls for the package. The async nature will call get_water in the users file before I am done with setting the bearer_token variable in my package.
Here is what my package looks like:
var request = require('request');
var bearer_token = "";
var api_url = "";
var Water = function(credentials){
api_url = credentials.api_url;
var username = credentials.username;
var password = credentials.password;
get_bearer_token(username, password, function(err, access_token){
bearer_token = access_token;
});
};
function get_bearer_token(username, password, callback){
var request_options = {
url: api_url + "/auth",
method: "GET",
'auth': {
'user': username,
'pass': password
}
};
request(request_options, function(err, res, body){
if(err) {
return callback("Water Initialization Error: " + err, null);
}
else {
return callback(null, body);
}
});
}
// Get water
Water.prototype.get_water = function(callback) {
var request_options = {
url: api_url + "/water",
method: "GET",
'auth': {
'bearer': bearer_token
}
};
request(request_options, function(err, res, body){
if(err) {
return callback(err, null);
}
else{
return callback(null, body);
}
});
};
// more package functions...
module.exports = Water;
I am trying to find a way to make the setup so the user can use the package as described above. I am not set on that style, but it seems to be the easiest for a user to understand and use.
The only way I could think of fixing this is add a callback to Water({}) but that would make a lot of mess having the user wrap all their water related code in the callback. I know it can be done by looking at the Twitter package for example, but I still could wrap my mind around how they did it. Any help would be appreciated, thanks.
You should delegate the responsibility of getting the token to when they make a request. That way the package is initialised instantly. To make it more efficient you can cache the token the first time it is fetched. I've made an example here, but you could tidy it up using something like async:
var request = require('request');
var Water = function (credentials) {
this.api_url = credentials.api_url;
this.username = credentials.username;
this.password = credentials.password;
return this;
};
Water.prototype.get_bearer_token = function (callback) {
// We already have the bearer token, so return straight away
if (this.bearer_token) return callback(null, this.bearer_token);
var self = this;
var request_options = {
url: this.api_url + "/auth",
method: "GET",
'auth': {
'user': this.username,
'pass': this.password
}
};
request(request_options, function(err, res, body){
if(err) {
return callback("Water Initialization Error: " + err, null);
}
else {
self.bearer_token = body;
return callback(null, body);
}
});
}
// Get water
Water.prototype.get_water = function(callback) {
var self = this;
this.get_bearer_token(function (err, token) {
if (err) return callback(err);
var request_options = {
url: self.api_url + "/water",
method: "GET",
'auth': {
'bearer': token
}
};
request(request_options, function(err, res, body){
if(err) {
return callback(err, null);
}
else{
return callback(null, body);
}
});
});
};
// more package functions..
module.exports = Water;
To use it:
var Water = require('water-reservation');
var water = new Water({
username: 'username',
password: 'pass',
api_url: 'http://example.com'
});
water.get_water(function (err, res) {
});

Resources