Sunday, February 22, 2009

Google Friend Connect finally support signed request

Why is that a big deal?

Here's the short answer; Now you can get the viewer id of a user loading your page and send it back to your server in a trusted way.

Before (up to two weeks ago, when I last tried it) it was only possible to send back info from the web page in an untrusted way.

What I mean by that is that you could load a custom opensocial gadget, which got the viewer id using the opensocial api, and then proceeded to use the opensocial makeRequest() call to route that information back to an url on your server. However, since this information orgiginated in the browser, potentially someone might somehow get a malign script to run on your page (from an ad, perhaps) and make up any old id, to then send back to your server.

In the opensocial specification, there is different modes of makeRequest, of which one is to make a signed request. To describe a signed request, I've drawn a small diagram above. Let's walk through it, and return to the signed request in a little while.

Let's say that you have copied in some Google Friend Connect gadgets on one of your pages, by registering your site with GFC, noting your sites code, and so on, the first thing that happens is that a person going to you site loads your page (1). In your page lies the gfc gadget references, which load the gadgets themselves from the GFC proxy servers (I call them proxies since much of what they do is route information back and forth third parties) (2). If this is the first time the users has been to the site and/or the user have not 'joined' the site on a GC sense, he/she may now proceed to do so (still (2)).

Now you have written your own, custom opensocial gadget, which is also loaded into the page in (2). It probably resides on your server, but could actually be loaded from any server on the internet. That gadget does the following;

a) Get the id of the viewer
b) tell the gfc/opensocial JavaScript api that is loaded to post the data to an url on your server. Two weeks ago, this could only be done without authentication.

This will send the data (viewer id) first to the gfc proxies(3), which will then route it forward to your server (4).
On your server is a special script that parses the post data out from the HTTP request (5).

The only thing that has changed now is that the makeRequest call can use the authentication mode gadgets.io.AuthorizationType.SIGNED, which will result in a 'stamp' made by the gfc proxies as the message passes through them. This is what it looks like in a small PHP script I wrote to collect the incoming signed info;


2009-02-22 12:41:36>---------------------> id=82146293318299142645, nick=psvensson
2009-02-22 12:41:36> post param -- the_user_id -> 32146007816295742145
2009-02-22 12:41:36> post param -- the_user_nickname -> psvensson
2009-02-22 12:41:36> get param -- opensocial_owner_id -> 03600513378691222179
2009-02-22 12:41:36> get param -- opensocial_viewer_id -> 78096296444182405045
2009-02-22 12:41:36> get param -- opensocial_app_id -> 09127246177732455082
2009-02-22 12:41:36> get param -- opensocial_app_url -> http://xxxyyyzzz.com/osaccess.xml
2009-02-22 12:41:36> get param -- xoauth_signature_publickey -> pub.1008283802.-8019269915578004945822.cer
2009-02-22 12:41:36> get param -- oauth_version -> 1.0
2009-02-22 12:41:36> get param -- oauth_timestamp -> 981102302897
2009-02-22 12:41:36> get param -- oauth_consumer_key -> friendconnect.google.com
2009-02-22 12:41:36> get param -- oauth_signature_method -> RSA-SHA1
2009-02-22 12:41:36> get param -- oauth_nonce -> 01831428822001149500
2009-02-22 12:41:36> get param -- oauth_signature -> MviLJcsxuU2tN9hQTkMEqZrEaC7ZUX31Cz7HH17I00vT2q8NJWH28OzvDab1Cl,01YtetX+Yln/IkuTj+I11SzFwZu5aXQda5D9HBeq+zjdxwWfuLGo62AaMjm5lvJGwWrMW6q+vm33MVOFWecxuXzSPmDfsCE9Tyf+b3M=

Numbers and other stuff are messed with a bit, due to posting :) But the thing is that two weeks ago, only the two POST lines were getting though, and any signed request returned and error in th browser.

This is the dawn of maybe not a new era, but a new eralischimo. Now you can use trusted user ids without having to manage the users, changing password, creating capthcas (and keeping up with the bots), listing friends and manage those connections, et.c. Now you just slam a gadget on the page, and get an id back you can trust. Not half bad eh?

Cheers,
PS

[UPDATE]

OK, this is a bit silly. There is more to this than just using signed makeRequests :) The reason I forgot this, is that the magic sauce was something I added in desperation some time ago when I tried to make signed requests work; I added a certificate to my domain using google accounts;

https://www.google.com/accounts/ManageDomains

Where you can upload a x.509 certificate that you can associate with your domain. The process is explained in more detail here;

http://code.google.com/apis/accounts/docs/RegistrationForWebAppsAuto.html

Including how to generate x.509 certs from scratch on Windows and Linux (Maybe mac as well).

2 comments:

John Weidner said...

Could you also pass to your gadget a parameter that identifies the user's session on your server? Then the gadget would pass that back to GFC so that when GFC sends you the user's id, you'd also get the user's session id. You could then keep the user's GFC id in the session of your user. That way the rest of the pages your render for that user would be able to be user specific.

Peter Svensson said...

@John Weidner: Yes, of course. As you can see in my example output, I added two post parameters myself into the request. I happened to take them from the GFC viewer info, but I could just as well have read the session id cookie, and passed that along.

Also, this partially solves the problem of needing the public key of GFC to verify that it is indeed GFC that send the data back to our server; having the original session id that was entered into the page where the custom gadget was loaded give us a moderate level of security.

Of course, our theoretical malign hidden script on the page could also read that cookie, but would need a server-side component which then faked the GFC proxy headers and any other viewer info. Doable, but semi-hard, and also void as soon as Google release the public key for the GFC proxies.

Cheers,
PS