Newbie - Geni.getStatus gives a frame restriction?

Started by Harald Tveit Alvestrand on Sunday, January 31, 2021
Showing all 13 posts

Complete newbie here..... trying to follow the API instructions in https://www.geni.com/platform/developer/help/sdk_js?version=1 to get to understand the API ..

I try this:

Geni.getStatus(response => {
console.log('Status is ', response.status);
status = response.status;
})
console.log('After status');

The result is an error message:

Refused to display 'https://www.geni.com/oauth/status?client_id=avRSGWx5oEjhAQEZIgD6xse...; in a frame because it set 'X-Frame-Options' to 'sameorigin'

I'm sure there's something simple I'm doing wrong. Perhaps trying to use jsdk.js at all, and direct HTTP requests would work better?

I am using jsdk.js by doing the OAuth login with a PHP script on server and then providing access token to Geni.init(). I could not get the authentication to work otherwise.

I had a similar problem. I "solved" it by skipping that part, I just use:

Geni.init({ app_id: '...' });
Geni.connect(function(geni_connection) {
if (geni_connection.status == 'authorized') {
// do stuff
}
});

which means needing to re-authorize each time the page is loaded, but that's okay for my current purposes

I've filed a ticket with Geni to see if they can address this.

Security is hard. When this Geni SDK JavaScript was written in 2011 it probably seemed like a good idea to provide a "status" call that would return an access token if the user had already authorized your app. When we implemented additional protections from cross-site scripting attacks last year, this getStatus call stopped working. Why? Because it was insecure.

There are various technologies intended to protect from cross-site attacks: CORS, Content-Security-Policy, X-Frame-Options, cookie directives, etc. The intent is to prevent a malicious site from making a hidden call to Geni's servers that would include the victim's Geni cookies (and therefore look like it's coming from the logged-in user). Imagine, for example, a POST to https://www.geni.com/account_settings/add_email that adds the hacker's email address to YOUR Geni account, thereby allowing him/her to request a password reset email and then take over your account.

Unfortunately the getStatus call worked in pretty much the same way: the only way it's useful is if Geni's API servers look at your login cookies to determine who you are and whether you've authorized the specified API application. If your application doesn't already have an access token for this user, getStatus was supposed to provide you one. But how? It could only do that if it knows who the user is, and without an access token the only way to know who the user is, is to look at the login cookies. So a malicious web page (and a sufficiently insecure browser) could be used to gain an access token to ANY Geni API application, if the user had already authorized that app. Not good! So getStatus has to be deprecated.

The work-around that Michael Shalom Fagan posted will work, but has the disadvantage of prompting the user to authorize your application every time connect is called, even if they've already authorized it (Michael can you confirm?). This would be disastrous on a traditional site where the user navigates from one page to the next (e.g. a Wordpress site) and the Geni SDK is reinitialized on each page. In that case you can initialize the Geni object with the access token that you previously obtained (and skip the connect call) but then you need to look out for expired access tokens.

We're working on a better solution for this, one in which you can store both the access token and refresh token (in cookies, or post it to a back-end server that you control, or browser local storage) and then the connect call would do everything necessary to do one of the following:

  1. Use the access token passed to Geni.init() if it's still valid
  2. Otherwise, use the refresh token passed to Geni.init() (if any)
  3. Otherwise, prompt the user to authorize your application.

It'll take a few days or maybe a week for us to tweak the code to handle that. I'll post here when it's released but in the meantime, just call connect unless you know you have a valid access token (in which case just pass that to Geni.init(access_token: <some_token>)

Yes, the process I'm using requires re-granting access each time. It works for my current use case, but obviously won't for everyone.

Since we're talking security, I think the API is requesting non-HTTPS URLs, so every single URL call results in a redirect to follow.

Michael Shalom Fagan yes we're fixing that, too...

thanks. okay, this isn't about security, but since I have your attention... :-)

Is the API being served from different servers from the website? I think I've been seeing data from the API lag behind updates made to a profile if less than a day before the API request. Not a big problem, just curious.

No, the API is hosted from the same servers, talking to the same database. I suppose you could be seeing a caching problem... the next time you notice it, capture what your API query was and what was the response, and either create a ticket on help.geni.com or start a discussion thread here and tag me.

Thanks for handling this!

So the advice is to use Geni.connect() unconditionally after Geni.init(), and wait for announcement of a better mechanism; Geni.status() should be considered removed.

Lets me get on with development!

thanks, Mike Stangel

Hi everyone,

We've released a few updates:

  1. The getStatus call is now deprecated and does nothing
  2. We've enabled refresh tokens in this JSDK. (Normally refresh tokens are discouraged for Oauth implicit flow, because the tokens are returned via HTTP redirect URLs which can then leak the tokens via HTTP_REFERER headers or the browser history; our JSDK doesn't use redirect URLs to return the tokens so this should be fine) Note: this was working for me during testing before the release, but I'm currently not seeing the refresh tokens. I'll continue working on that.
  3. You can read Geni._access_token and Geni._refresh_token and store them somewhere secure (browser local storage, post back to your server, cookies, etc). You can then pass those to Geni.init() to bypass the authorization screen. Note that if you call Geni.init() with cookies: true all of this will happen automagically (once I fix whatever's failing to return the refresh token) -- however be advised that cookies don't work if you're loading your HTML page from a file.
  4. If you call Geni.init with an access token (or with cookies: true and there's an access token already stored from a previous call) then you can call connect() and it will short-circuit and NOT prompt the user to authorize your app. If at any time an API call throws an OAuthException, the refresh token will be used automatically to try to get an updated access token.

We're still smoothing out the rough edges but we're getting closer to a seamless experience.

Update: refresh tokens are now working with the JSDK.

Showing all 13 posts

Create a free account or login to participate in this discussion