Polling Google sites for changes - node.js

I need an event that is being fired whenever something changes on my google sites site.
There is an XML feed with all the latest changes. I am planning on polling this feed with Zapier and when something changes Zapier will make a http request to a url that I provide so I get my event.
This works fine as long as the site is public, but in my case it is not.
So I think about building a proxy for the feed that google provides. The proxy will call the feed with the proper authentication and pass the contents. Zapier will call the proxy with a Zapier friendly authentication mechanism.
I figured that I need to call the google feed with a service account. So how do I do that with node.js?
I have been looking here:
https://developers.google.com/google-apps/sites/docs/1.0/developers_guide_protocol#ActivityFeed

Figured it out!
I have made a proxy with basic authentication that is accessed via https. This proxy will pass through the activity feed of the Google site that I am interested in.
I built the proxy in node.js with the googleapis module. Here is a piece of the code I use:
var jwtClient = new googleapis.auth.JWT(
client_id_email,
__dirname + 'key.pem',
null, ['https://sites.google.com/feeds/'],
'user#domain.org'
);
jwtClient.authorize(function(err, token) {
if (err) return next(err);
return rest({
path: 'https://sites.google.com/feeds/activity/{domainName}/{siteName}',
headers: {
"GData-Version": "1.4",
Authorization: token.token_type + ' ' + token.access_token
},
params: {
domainName: 'domain.org',
siteName: 'site',
},
}).done(function(result) {
res.set(result.headers);
res.send(result.entity);
}, next);
});
The username (user#domain.org) in the example is a user that the service account impersonates. This user must have acces to the site.
I put the service account key in a file name 'key.pem' in this example.
Now you must allow accesd to the service account for the domain you want to access. You do this on the admin site of the domain (admin.google.com).
Go to security
Go to advanced settings
Go to API client access
There you have to add the client_id of the service client
And now... it works :-) !!! \o/

Related

How to customize remote api call?

I write a signup webpage with nodejs, and in this webpage, I use ajax to call the function signup like this
$.ajax({
method: "POST",
url: "/signup",
data: { tel: tel, password: password}
})
And in app.js, the signup function like this
.post('/signup', async (ctx) => {
//do something
})
And now everyone can call the signup function with the url http://domain/signup without visiting the signup webpage, I think it's a mistake, I only want the local program can call this function, how can I fix this?
Typically it's either API Keys for doling out general access, or IP-based restrictions at either the application or network level.
API Keys are a token that identifies and authenticates an endpoint. You can also use it to track usage and/or ban abuse. For example, see Google Maps' documentation about using their API. Then all API calls have that key:
https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap
This allows the server to parse the key, check against it's key database or whatever, and allow access. You'll need to use HTTPS for this if it's over any public network.
IP or other network restrictions are easier to setup and best when you have a 1:1 relationship with your API. That is, your application alone accesses this API, and you control all the servers, etc.

401 Authorization Required integrating Hyperledger Composer REST API from Webapp

Introduction
I have a hyperledger env running in secure mode by following this link https://hyperledger.github.io/composer/integrating/enabling-rest-authentication.html
and it works fine if I authenticate as specified in the document (hitting http://mydomain:3000/auth/github directly from the browser) and then access the Rest API from the http://mydomain:3000/explorer and could authorize as various participants (i.e, issuing identity and adding them to the wallet and setting one as default at a time) and could see the assets as per the .acl file.
Issue
But I started facing problems when I started integrating the Rest API's from my web application rather directly from the browser. As a first step from my web app, I called the http://mydomain:3000/auth/github to authenticate and then started calling the other APIs (transaction/list, etc.) but I do always get
Error 401: 'Authorization Required'
What i have tried
Gave my web application URL as the 'Redirect URL' in the env variable for the hyperledger. And upon successful authentication (calling http://mydomain:3000/auth/github) it successfully redirected to my webapp home page but afterwards accessing the Rest API's (from web app) again throws 'Authorization Required' error.
Environment variaable as below:
export COMPOSER_PROVIDERS='{
"github": {
"provider": "github",
"module": "passport-github",
"clientID": "CLIENT_ID",
"clientSecret": "CLIENT_SECRET",
"authPath": "/auth/github",
"callbackURL": "/auth/github/callback",
"successRedirect": "http://localhost:8080/home.html",
"failureRedirect": "/"
}
}'
Incorporated passport-github2 mechanism in my web application (i.e, registered my app with the oauth of github) and upon successful login to my web application; called the http://mydomain:3000/auth/github to authenticate to the blockchain and it did not work out as well.
I have a few questions:
Is it feasible to call the secured hyperledger Rest API's from another web application?
If Yes, how to do it? I don't find that information in the hyperledger composer documentation.
Have been trying this for a week now and have no answers. Any help would be greatly appreciated. Please let me know if anything is unclear. Thanks.
I commented about this problem on one of the existing hyperledger github issues(below link) & I want to share the solution that solved this problem for me.
https://github.com/hyperledger/composer/issues/142
Solution: as mentioned by user sstone1
Since the REST server is on a different port number to your web application, you need to specify an additional option to your HTTP client to pass the cookies to the REST server. Using the Angular HTTP client, you add the withCredentials flag, for example:
via Angular:
this.http.get('http://mydomain:3000/api/MyAsset', { withCredentials: true })
via JQuery AJAX:
$.ajax({
url: 'http://mydomain:3000/api/MyAsset',
xhrFields: {
withCredentials: true
},
headers: {
...
}
})

create oauth2 isn't working

I'm trying to build a web app on Meteor with Outlook mail API. The first step is to get user authentication through the Outlook mail API, which requires a custom URL with local host, client id, secret, etc attached.
I've looked at a few node.js oauth2 tutorials and downloaded simple-oauth2. All the tutorials use something like:
const oauth2 = require('oauth2').create({
client: {
id: '<client-ID>',
secret: '<app-password>',
},
auth: {
tokenHost: 'https://login.microsoftonline.com',
authorizePath: 'common/oauth2/v2.0/authorize',
tokenPath: 'common/oauth2/v2.0/token'
}
});
When I run it, the console logs "Uncaught TypeError: require(...).create is not a function." Is create() something that comes with some sort of download that I don't have? The require works fine if I split it up into two parts, so I know it's an issue with create().
First of all wenn you list const then don´t extend them at the end only you know what you do.
Second I don´t use Outlook API but I use OneDrive API and it should be similar. You need to follow the docs about how to get the auth code. Or here my guide for creating it for OneDrive should be same to your problem
Add application at https://apps.dev.microsoft.com/ you need microsoft account for this.
Input your Application Name. In this case, don't use Guided Setup
Create Application secret and copy it to save loaction
Add Platform and choose web. In this case, redirect URL is http://localhost/dashboard
Go to Graph access below and choose for both options Files.ReadWrite.All
Make sure you press save.
Enter your client ID in this url where xxxxxx is. Notice that client ID is the Application ID you can find at the top of your application settings.
https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=xxxxxxxxxx&scope=offline_access%20files.readwrite.all&response_type=code&redirect_uri=http://localhost/dashboard
Please inport above URL to your browser. Log in with your microsoft account and then give your application access by clicking yes. Then you will get redirected to http://localhost/dashboard/?code=xxxxxxxxx-xxxxxx-xxxxx-xxx-xxxxx
This auth code can be used for the access_token that will be generated with this step
request.post({url:'https://login.microsoftonline.com/common/oauth2/v2.0/token', form: {
redirect_uri: 'https://login.live.com/oauth20_desktop.srf',
client_id: 'xxxxxxxxxxxxxxxxx',
client_secret: 'xxxxxxxxxxxxxxxx',
code: 'xxxxxxxxxxxxxxxxxx',
grant_type: 'authorization_code'
}
}, function(err,httpResponse,body){ /* ... */
console.log('err: ' + err)
console.log('body: ' + body)
});
This is basicly the same process for every oAuth2 API. Notice that I use for other API´s in the access_token request the redirect_uri http://localhost/dashboard
If you make a request on and console.log as example the body and you get object object as result then please use stringify. Hope it could help you

CAML-query to Sharepoint REST API results in "invalid audience URI"?

We have been successfully using the Sharepoint REST API to retrieve items from lists for a while now, but just recently decided to integrate the ADAL.JS in order to be able to access other Microsoft APIs such as Graph, Azure AD etc.
After successfully authenticating Adal.js automatically adds an
Authorization: Bearer eyJ..
header to the REST calls which works fine after fiddling with permissions a bit. The app is an Angular SPA hosted in Sharepoint so this header isn't necessary but doesn't really matter.
HOWEVER, a few of our REST calls require us to also query the taxonomy and as that isn't supported in the normal Sharepoint REST API, we have to hit the (/_api/web/lists/GetByTitle('ListName')/GetItems endpoint) with a CAML-query as request payload i.e. https://mydomain.sharepoint.com/sites/dev/_api/web/lists/GetByTitle('News')/GetItems
Unfortunatelly, this does not work as the API simply returns
Invalid audience Uri '5exx5cef-x7xx-4xxx-axxx-4xxxx2e40'.
So far my only solution is to modify the actual Adal.JS library to remove this header for this specific endpoint.
So, my questions is - has anyone done CAML-queries against Sharepoint REST APIs using Adal.JS, or ran into a similar problem and can provide any insight?
I suspect it is a configuration issue but am somewhat at loss on what to do.
In this case, you need to force setting the endpoint 'https://mydomain.sharepoint.com' to null. Else, each request to "mydomain.sharepoint.com" will add a graph authorization header which be validated by the SharePoint server. Since the app is registered on the Azure AD rather than SharePoint, it will be considered as a invalid audience.
Here is the workaround for your reference, please let me know if it works on your side.
(function () {
angular.module('app', [
'ngRoute',
'AdalAngular'
]).config(config);
// Configure the routes.
function config($routeProvider, $httpProvider, adalAuthenticationServiceProvider) {
$routeProvider
.when('/', {
templateUrl: 'views/main.html',
controller: 'MainController',
controllerAs: 'main'
})
.otherwise({
redirectTo: '/'
});
// Initialize the ADAL provider with your clientID (found in the Azure Management Portal) and the API URL (to enable CORS requests).
adalAuthenticationServiceProvider.init(
{
clientId: clientId,
// The endpoints here are resources for ADAL to get tokens for.
endpoints: {
'https://graph.microsoft.com': 'https://graph.microsoft.com',
'https://mydomain.sharepoint.com': null
}
},
$httpProvider
);
};
})();

Oauth2 flow without redirect_uri

I am creating an Android/iOS app which communicates with a Node.js server and would like to identify them securely on my server using Google (and/or Facebook) and OAuth2. I've looked at the following documentation: https://developers.google.com/+/web/signin/server-side-flow
I do not need authorization, I only need authentication (I only want to make sure that the person calling my Node.js service is the person they say they are). To achieve this, if I understand properly, I have to let the user log in using Google on the client side, this will give them an authorization_code which they can then give to my server. My server can then exchange that code for an access_token, and therefore retrieve information about the user. I am then guaranteed that the user is the person they say they are.
The Google documentations (link above) says: "In the Authorized redirect URI field, delete the default value. It is not used for this case.", however, for my server to exchange the authorization_code for an access_token, it needs to provide a redirect_uri, am I missing something?
The redirect_uri is useless for Unity games, for instance (since logging in with Google simply opens a new "window", which is closed when logged in, no redirection involved).
TL;DR
How do you use OAuth2 to authenticate users between my client and my server without redirection?
TL;DR How do you use OAuth2 to authenticate users between my client and my server without redirection?
You can't. OAuth requires that the user is directed to an authorization (and possibly login) screen, and then redirected back to your app.
EDIT 20/12/22. See comment below regarding latest status
Have you looked at this documentation? https://developers.google.com/accounts/docs/OAuth2InstalledApp#choosingredirecturi
Choosing a redirect URI
When you create a client ID in the Google Developers Console, two
redirect_uris are created for you: urn:ietf:wg:oauth:2.0:oob and
http://localhost. The value your application uses determines how the
authorization code is returned to your application.
http://localhost
This value signals to the Google Authorization Server that the
authorization code should be returned as a query string parameter to
the web server on the client. You may specify a port number without
changing the Google Developers Console configuration. To receive the
authorization code using this URL, your application must be listening
on the local web server. This is possible on many, but not all,
platforms. If your platform supports it, this is the recommended
mechanism for obtaining the authorization code.
I had this problem and it took me ages to find the "postmessage" solution that Nepoxx mentions in the comments of the accepted answer here.
For clarification, here's what worked for me.
Follow steps 1-6 here: https://developers.google.com/identity/sign-in/web/server-side-flow
Install googleapis library npm install --save googleapis
For the server-side token exchange do this:
var googleapis = require('googleapis');
var OAuth2 = googleapis.auth.OAuth2;
var oauth2Client = new OAuth2(
GOOGLE_SSO_CLIENT_ID,
GOOGLE_SSO_CLIENT_SECRET,
'postmessage' // this is where you might otherwise specifiy a redirect_uri
);
oauth2Client.getToken(CODE_FROM_STEP_5_OF_INSTRUCTIONS, function(err, tokens) {
// Now tokens contains an access_token and an optional refresh_token. Save them.
});
The redirect_uri can be a URL with a custom URL scheme for which the client registered a handler. This is described here: What's a redirect URI? how does it apply to iOS app for OAuth2.0?. It is not so much about "redirecting" it is about a callback endpoint to your app.
And it become really easy if you use VueJS with https://github.com/guruahn/vue-google-oauth2
Client side
import GAuth from 'vue-google-oauth2'
Vue.use(GAuth, {
clientId: 'xxxxxxx.apps.googleusercontent.com',
scope: 'profile',
})
async signWithGoogle() {
const code = await this.$gAuth.getAuthCode() //
console.log(code ) // { code: 'x/xxxxxxxxxx' }
// send the code to your auth server
// and retrieve a JWT or something to keep in localstorage
// to send on every request and compare with database
}
Server side
import { google } from 'googleapis'
const oauth2Client = new google.auth.OAuth2(GOOGLE_ID, GOOGLE_SECRET, 'postmessage')
google.options({ auth: oauth2Client })
async function getAccount(code) {
// the code you sent with the client
const { tokens } = await oauth2Client.getToken(code)
oauth2Client.setCredentials(tokens)
const oauth2 = google.oauth2({ version: 'v2' })
const { data: { id } } = await oauth2.userinfo.get()
// there you have the id of the user to store it in the database
// and send it back in a JWT
}

Resources