Google App Engine Channel API javascript location - google-app-engine

I'm a bit confused about this, and want to make sure I'm not being naive about how this works. Does the client side javascript have to also be hosted on Google App Engine? Say I create a channel on my dev server, then I have a local HTML file (non-hosted) on my computer with the required javascript, and I connect to that channel with a token - will that work? Or is this not how channels work?
Edit:
All I have is an HTML file in the same directory as my app.yaml file (so the root directory for my website). I'm in the devserver.
First I create a channel and get the token:
token = channel.create_channel('1')
print token
>>> channel-4132644671-1352248413-1
Then I copy that token in my HTML file:
<html>
<head>
<script type="text/javascript" src="http://localhost:8080/_ah/channel/jsapi"></script>
</head>
<body >
<script>
var token = 'channel-4132644671-1352248413-1';
var channel = new goog.appengine.Channel(token);
var socket = channel.open();
socket.onopen = function() { alert('open'); };
socket.onmessage = function() { alert('message'); };
socket.onerror = function() { alert('error'); };
socket.onclose = function() { alert('close'); };
</script>
</body>
</html>
I open the HTML file with Safari. I get an alert saying "open". However, no matter what token I type in var token, I get an "open" alert, so I'm not sure if getting that alert means anything.
Then I do:
channel.send_message('1', 'hi')
And nothing happens in my HTML file. No alerts. What am I doing wrong?

Because of the same origin policy, the script must be hosted on the same domain as the server that the app opens the channel to. With the present implementation, that server is talkgadget.google.com and the supporting script is https://talkgadget.google.com/talkgadget/channel.js. With curl you can see that the /_ah/channel/jsapi endpoint of your app simply issues a 302 redirect to that script. So unless you intend to develop and run your own channel server, no, you can't do this.
Furthermore, if the underlying implementation of channels should happen to change in the future, App Engine would be updated to redirect /_ah/channel/jsapi to a new script so existing apps would continue to work, while a custom approach would likely break. One less reason to do it yourself.

In the dev_appserver, the channel is implemented by a javascript function that's constantly polling the server.
If your dev_appserver isn't actually running (it looks like you've somehow broken into dev_appserver), the polling function isn't going to succeed and you won't get the channel messages.
On production, it looks like the channel API uses some sort of long polling.
Also, I'd have to double check the docs, but I believe the first param to send_message should be the token.

Related

Post xhr blob request (300kb) failed with Firefox and App engine flexible environnement

When I test my website in SSL context with load balancer. Impossible to post a big GZIPed content (300kB) . The request freeze in firebug. If the request lighter, it works.
It works perflecty with Chrome or others browsers.
I don't see log request on the app engine instance. Don't see log in the nginx. Don't see log on load balancer during the request. When the request fail, I see logs and the logs say it take 60seconds (timeout)
I see the same problem here :
Ajax post being aborted by firefox (not seen in Chrome or IE)
(Using fiddler proxy also worked)
Tried with "async: false" does not worked.
Tried to use "setTimeout" around the ajax call does not worked.
Note that when I use the app directly to the instance without load balancer, opening the ports, it work perflectly.
It work perfectly in localhost with SSL configured. (self signed certificate)
Note that when I use the app without SSL and with the load balancer it work perflectly.
The request appear in firebug with empty information (the last request) and stay like this and without return code.
Following here, it could be relative to the load balancer configuration.
Firefox AJAX POST w/ FormData Never Completes
But it's the Google App engine load balancer. I don't have the hand on it and don't know the stack.
Equivalent problem here :
ajax request not work in Firefox
Also, when I use Fiddler proxy to intercept the request and decrypt it, it works !
I used formData to send the GZIP data and it works now.
var formData = new FormData();
var blob = new Blob([binary]);
formData.append("file", blob);
that.postDataBody = formData;
instead of
that.postDataBody = new Blob([binary]);

How to implement Azure AD single tenant auth in React and standalone Golang Web API?

We have a React application created with Create-React-App. Right now, it's being served with nginx:alpine in a docker container in Azure App Services. I don't care much about the server since it only serves the built react app as static files.
Aside from that, we also have a golang api running standalone in another container in Azure App Services. Now what we want to do is when a user points to the React app URL, he/she will be redirected to login.microsoft.com/xxxxx..... or the popup will show. User will signin, then that will be the time he can access the application.
I've read the docs but being fairly new to these concepts, and to web development in general, I'm confused a bit on how to start this. I understand the flow but I can't implement it yet.
I've tried the Browser to Web App scenario in NodeJS but I guess it is for server side rendering. So my option is to use ADAL which does not have React implementation in their docs. And their ADAL JS samples have the spa being served at the same app with the API.
Now, for my specific situation, React's server is different than the API. So I don't really know how to continue. Can someone at least point me to a working example of my situation? Or enumerate what I have to do? I don't need code, just a method that will take me there.
But if this can be done correctly, I think I'll do the method below:
Web Browser to Web Application scenario
// Use the passport-azure-ad and OIDC strategy in the docs and samples
// Configure express
app.get('/', function(req, res){
if (!req.user) {
// res.render the template which has link to login
} else {
// res.sendFile the React's index.html
}
});
app.get('/login',
passport.authenticate('azuread-openidconnect', { failureRedirect: '/login'
}),
function(req, res) {
log.info('Login was called in the Sample');
res.redirect('/');
});
app.get('/logout', function(req, res){
req.logout();
res.redirect('/');
});
Although I doubt it because I tried the method above and it only served the html and chrome says "http://localhost:3000/static/css/xxxxxxxx.css" and the JS file both don't exist even though I pointed it correctly in the public folder.
Also, with the above method, I can't use the app.get("*", xxxx) which I need to prevent "File Not Found" when browser is refreshed.
So if I can't do it like the above method, what's the solution?

ADAL.JS with Mobile App

I'm trying to integrate some Oracle delivered Mobile Application Framework Apps (MAF) mobile apps with Azure AD authentication. I have tried the Java approach, which apparently doesn't work in my case.
So I decided to try using a Javascript login page option using ADAL.JS. Since MAF creates cross-platform compatible code by transpiling to HTML 5/Javascript/Cordova, I reckoned I could make the JS option work without resorting to having multiple SDK specific solutions like ADAL-Android or ADAL-IOS. Since I can wrap it all in an HTML page as I can use the OAUTH implicit flow option that ADAL.JS requires. I have the ADAL.JS part working from my PC using this example with a local Node/Webpack dev server for the redirect URI. (Note, just like that example, I'd prefer to use the strict adal.js option and avoid any angular-js stuff). However, I'm running into an issue when deployed on the Android mobile device. It appears to be due to the reply URI. After being prompted for Azure credentials and supplying those, the following error is produced.
AADSTS50011: Reply address 'file:///data/user/0/com.company.app/storage/assets/FARs/ViewController/public_html/SignOn/login.html' has an invalid scheme.
I found that when deploying to a mobile device the Azure registered app must be set to type "Native" instead of "Web/API" which I have done. And according to an MSFT example (which I cannot include since I don't have enough rep to include more than two links) the redirect URI must be set to "https://login.microsoftonline.com/common/oauth2/nativeclient". But I still get the same error.
UPDATE since #FeiXue Reply
I'm using the original endpoint not 2.0. When I set the redirectURI as such:
redirectURI=https://login.microsoftonline.com/common/oauth2/nativeclient
The browser returns this in the address bar and remains there on a blank screen and does not issue a token. It does this both on the PC browser and mobile browser.
http://login.microsoftonline.com/common/oauth2/nativeclient#id_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6ImEzUU4wQlpTN3M0bk4tQmRyamJGMFlfTGRNTSIsImtpZCI6ImEzUU4wQlpTN3M0bk4tQmRyamJGMFlfTGRNTSJ9.(shortened for brevity)&state=e1ce94fb-6310-4dec-9e8b-053727ceb9b8&session_state=1beafa4d-af55-415b-85d5-83e8b4035594
However, for the exact same code, on the PC when I set the redirectURI as such it returns an access token:
redirectURI=https://localhost:8443 <-- port to my local node server
I've also tried it with a redirectURI of urn:ietf:wg:oauth:2.0:oob, but that does not work either.
Code
<!DOCTYPE html>
<html>
<head>
<title>Authenticate User with ADAL JS</title>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://secure.aadcdn.microsoftonline-p.com/lib/1.0.0/js/adal.js"></script>
<script type="text/javascript">
$(document).ready(function() {
"use strict";
var variables = {
azureAD: "mytenant.onmicrosoft.com",
clientId: "cc8ed7e0-56e9-45c9-b01e-xxxxxxxxxx"
}
window.config = {
tenant: variables.azureAD,
clientId: variables.clientId,
postLogoutRedirectUri: window.location.origin,
redirectUri: "https://login.microsoftonline.com/common/oauth2/nativeclient",
endpoints: {
aisApiUri: "cc8ed7e0-56e9-45c9-b01e-xxxxxxxxxx"
}
//cacheLocation: "localStorage"
};
var authContext = new AuthenticationContext(config);
var isCallback = authContext.isCallback(window.location.hash);
authContext.handleWindowCallback();
if (isCallback && !authContext.getLoginError()) {
window.location = authContext._getItem(authContext.CONSTANTS.STORAGE.LOGIN_REQUEST);
}
var user = authContext.getCachedUser();
if (!user) {
authContext.login();
}
authContext.acquireToken(config.endpoints.aisApiUri, function (error, token) {
if (error || !token) {
console.log("ADAL error occurred in acquireToken: " + error);
return;
}
else {
var accessToken = "Authorization:" + " Bearer " + token;
console.log("SUCCESSFULLY FETCHED TOKEN: " + accessToken);
}
});
});
</script>
</head>
<body>
<h1>Test Login</h1>
</body>
</html>
Update
#FeiXue So I guess from what you're saying the id_token IS the access token? I think then the problem is this.
When the redirectURI="https://localhost:8443" it redirects back to my index.html after AAD login and the authContext.acquireToken() works and returns a valid token.
But when the redirectURI="https://login.microsoftonline.com/common/oauth2/nativeclient" it never redirects back from http://login.microsoftonline.com/common/oauth2/nativeclient#id_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1Ni......
While it shows the id_token, it never redirects back to my index.html So I can't make a call to authContext.acquireToken() for passing it onto my web API.
From my research on this topic here is the gist on ADAL.JS and Native (Mobile) Device Support
As #fei-xue-msft mentioned, ADAL.JS is not intended for nor does it work with native/mobile devices. ADAL.JS was written with the “original” Azure endpoint in mind, not the v2.0 endpoint that provides more functionality for mobile/native devices (see more below on the two different endpoint options). There is however an experimental ADAL.JS branch you can try (uses the v2.0 endpoint), but it is not not being actively updated anymore so you are on your own. The new MSFT approach is to use the new MSAL library, which is written towards the v2.0 endpoints. However there is no MSAL-for-JS library yet but rumor is there will be one at some point. For more on the two different Azure endpoints (“original” versus “v2.0”) see the links below. The confusion over this was a source of frustration in my troubleshooting so I help this helps some others going down this track.
So if you are looking to get Azure Oauth authentication on mobile devices, first decide which Azure Endpoint you want to use (Supporting links on that below as v2.0 does have some restrictions that the original endpoint does not). You can determine what your specific endpoints for your tenant are by viewing the Metadata Doc links listed below, just substitute your tenant name or ID. You should be able to use either.
To register an application for a specific type of endpoint (original versus v2.0) use the appropriate App Registration Portal link cited below. Then, to decide what your options are for creating an Azure auth solution for native/mobile device, see the code samples for each endpoint version, and make sure the sample is for “native” else it probably won’t work on your mobile device. For example, you will not see an ADAL.JS sample for the original endpoint library options, but you will see one for Cordova (which is why #fei-xue-msft suggested that approach). For the v2.0 endpoint samples you will see the MSAL/Xamarin options, and for an Javascript option you can try something like the Hello.JS Sample.
Original Endpoint
https://login.microsoft.com/{tenant id}/oauth2/authorize
App Registration Portal: https://portal.azure.com
Code Samples: https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-code-samples#native-application-to-web-api
Native Auth Scenarios: https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-authentication-scenarios#native-application-to-web-api
OpenID Metadata Doc: https://login.microsoft.com/{tenant id}/.well-known/openid-configuration
V2.0 Endpoint
https://login.microsoftonline.com/{tenant id}/oauth2/v2.0/authorize
App Registration Portal: https://apps.dev.microsoft.com
V2.0 Endpoint Compare: https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-v2-compare
Code Sample: https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-v2-libraries
OpenID Metadata Doc: https://login.microsoft.com/{tenant id}/v2.0/.well-known/openid-configuration
Are you developing with Azure AD V2.0 endpoint?
If not, we are able to config the redirect URIs as we want on the portal for the native app. However as the error message indicates that the file protocol is not a a validate scheme.
In this scenario, we can use the http or https since you were developing with HTML.
And in the Azure AD V2.0 endpoint, we are not able to set the redirect_Uri for the native app at present. We can use urn:ietf:wg:oauth:2.0:oob or https://login.microsoftonline.com/common/oauth2/nativeclient for the redirect_Uri. The first one is used for the native app for the device and the second we can use for the client which host in browser(web-view).
At last, please ensure that the redirect_uri in the request is using the correct one you register for the portal. You can also test the request on the browser to narrow down whether this issue was cause the incorrect redirect_uri in the request. And for the authorization request, you can refer links below:
Authorize access to web applications using OAuth 2.0 and Azure Active Directory
v2.0 Protocols - OAuth 2.0 Authorization Code Flow
Update(there is no href property if open the HTML from disk which cause the popup page is not closed)
AuthenticationContext.prototype._loginPopup = function (urlNavigate) {
var popupWindow = this._openPopup(urlNavigate, "login", this.CONSTANTS.POPUP_WIDTH, this.CONSTANTS.POPUP_HEIGHT);
if (popupWindow == null) {
this.warn('Popup Window is null. This can happen if you are using IE');
this._saveItem(this.CONSTANTS.STORAGE.ERROR, 'Error opening popup');
this._saveItem(this.CONSTANTS.STORAGE.ERROR_DESCRIPTION, 'Popup Window is null. This can happen if you are using IE');
this._saveItem(this.CONSTANTS.STORAGE.LOGIN_ERROR, 'Popup Window is null. This can happen if you are using IE');
if (this.callback)
this.callback(this._getItem(this.CONSTANTS.STORAGE.LOGIN_ERROR), null, this._getItem(this.CONSTANTS.STORAGE.ERROR));
return;
}
if (this.config.redirectUri.indexOf('#') != -1)
var registeredRedirectUri = this.config.redirectUri.split("#")[0];
else
var registeredRedirectUri = this.config.redirectUri;
var that = this;
var pollTimer = window.setInterval(function () {
if (!popupWindow || popupWindow.closed || popupWindow.closed === undefined) {
that._loginInProgress = false;
window.clearInterval(pollTimer);
}
try {
//there is no href property if open the HTML from disk
if (popupWindow.location.href.indexOf(registeredRedirectUri) != -1) {
if (that.isAngular) {
that._onPopUpHashChanged(popupWindow.location.hash);
}
else {
that.handleWindowCallback(popupWindow.location.hash);
}
window.clearInterval(pollTimer);
that._loginInProgress = false;
that.info("Closing popup window");
popupWindow.close();
}
} catch (e) {
}
}, 20);
};
This issue is caused that when we open the HTML page from device(disk), the parent HTML page(login page) is not able to get the location of the popup page. So the parent page is not able to close that page based on the location of popup page. To workaround this issue, I suggest that you developing with azure-activedirectory-library-for-cordova or host the login page on the back end of web API.

Getting No response from google drive javascript api

I'm using google drive javascript api(v2) in my reactjs project.
And I'm using this function to get files from google drive.
this.getProjectObjects = function(query, callback)
{
var request = gapi.client.drive.files.list({
corpus : 'DEFAULT',
q : query,
fields : 'items(id,description,title,properties)'
});
request.then(function(resp) {
callback(resp.result.items)
}, function(err) {
console.log(err)
});
};
After authenticate the user, I can get the files using this function.
But, when I navigate to other react component, it's no longer working.
There is no response or error, api just hangs.
Silly thing is when I refresh page, it's working. Again if I navigate to other component, it's not working.
I'm using the same query, nothing changed during navigating.
Anyone have any ideas what the issue could be?
Thanks
You may want to check the Chrome devTools to see if you are encountering any problem when switching pages. Also in the documentation - Getting Started:
There are several ways to use the JavaScript client library to make API requests, but they all follow the same basic pattern:
The application loads the JavaScript client library.
The application initializes the library with API key, OAuth client ID, and API Discovery Document(s).
The application sends a request and processes the response.
The following sections show 3 common ways of using the JavaScript client library.
Option 1: Load the API discovery document, then assemble the request.
Option 2: Use gapi.client.request
Option 3: Use CORS
See the sample for code implementation on effectively loading and making API calls.
Hope this helps.

Fetch data from Google Analytics API

What I Want: The website should show the analytics data without Authorization.
What I Did: I have a app on google app engine and enabled API and created service account which gave a json file.
I tried to do same as: https://ga-dev-tools.appspot.com/embed-api/custom-components/ but didn't succeed.
Then I came across this issue: http://code.google.com/p/analytics-issues/issues/detail?id=496 and changed my code as this
<!DOCTYPE html>
<html>
<head>
<title>Embed API Demo</title>
</head>
<body>
<!-- Step 1: Create the containing elements. -->
<section id="auth-button"></section>
<section id="view-selector"></section>
<section id="timeline"></section>
<!-- Step 2: Load the library. -->
<script>
(function(w,d,s,g,js,fjs){
g=w.gapi||(w.gapi={});g.analytics={q:[],ready:function(cb){this.q.push(cb)}};
js=d.createElement(s);fjs=d.getElementsByTagName(s)[0];
js.src='https://apis.google.com/js/platform.js';
fjs.parentNode.insertBefore(js,fjs);js.onload=function(){g.load('analytics')};
}(window,document,'script'));
</script>
<script>
gapi.analytics.ready(function() {
// Step 3: Authorize the user.
gapi.analytics.auth.authorize({
serverAuth: '<server auth key>'
});
// Step 4: Create the view selector.
var viewSelector = new gapi.analytics.ViewSelector({
container: 'view-selector'
});
// Step 5: Create the timeline chart.
var timeline = new gapi.analytics.googleCharts.DataChart({
reportType: 'ga',
query: {
'dimensions': 'ga:date',
'metrics': 'ga:sessions',
'start-date': '30daysAgo',
'end-date': 'yesterday',
},
chart: {
type: 'LINE',
container: 'timeline'
}
});
// YOU MUST CALL THIS MANUALLY HERE INSTEAD OF WAITING FOR CALLBACK
viewSelector.execute();
// Step 6: Hook up the components to work together.
gapi.analytics.auth.on('success', function(response) {
viewSelector.execute();
});
viewSelector.on('change', function(ids) {
var newIds = {
query: {
ids: ids
}
}
timeline.set(newIds).execute();
});
});
</script>
</body>
</html>
I tried seeing couple of documents to get server auth key, but I failed getting it.
Can anyone help in setting the server auth key and by this will my purpose of displaying charts without authentication will resolved?
Thanks,
Sharad Soni
Embeded API Getting started
The Embed API handles almost all of the authorization process for you
by providing a one-click sign-in component that uses the familiar
OAuth 2.0 flow. In order to get this button working on your page
you'll need a client ID.
The embedded API was designed to work with Oauh2 and ask a user for authentication it is not designed to work with a service account. I have never seen code for a service account working in JavaScript. That is probably due to the fact that a lot of people myself included do not feel that a service account in client sided JavaScript would be secure, and may even be against Goggles new terms of use.
If you want to use a service account you will need to switch to a server sided language for authentication. the embedded API simply uses Google charts so you can also code that manually.

Resources