Paul Osman, Dan Mills, and I recently attended the Federated Social Web Summit and a commonly mentioned problem was discovery in a de-centralized environment. How do you get website A to talk to website B without a centralized service to coordinate? This post takes a stab at solving this problem with a progressive enhancement that works on Firefox 3.5 and later.
Here is a short screencast to describe an example problem and showing the solution in action.
Mo Federation Mo Problems
The basic issue is that due to web browser’s same origin policy,http://bob.example.com
cannot know that Alice uses http://alice.status.net
as her preferred Social Networking application for OStatus interactions. This isn’t an issue on today’s monoculture social networking web, but as we move to embrace website diversity, we need a way to discover URLs and other details that are personalized to the current user.
The registerProtocolHandler postMessage Pattern
So what’s in the magic sauce in the video, you ask? Two JavaScript methods: registerProtocolHandler and postMessage.
After Alice logs in on http://alice.status.net
, the following JavaScript code runs:
if ('navigator' in window && 'registerProtocolHandler' in window.navigator) { navigator.registerProtocolHandler( "ostatus", "http://alice.status.net/follow?uri=%s", "OStatus"); }
When Alice visits http://bob.example.com/bob
and clicks Subscribe, an <iframe>
is created with its URL pointed to ostatus://bob.example.com/bob/follow
. Note the scheme in the URL is ostatus and not http.
$(window).bind('message', function(event) { $('input[name=profile]').attr('value', event.originalEvent.data); }); $('body').append("<iframe id='ostatus_follow_frame' src='ostatus://bob.example.com/bob/follow'></iframe>");
Firefox invokes Alice’s protocol handler and the actual request is for http://alice.status.net/follow?uri=ostatus%3A%2F%2Fbob.example.com%2Fbob%2Ffollow
. Alice is an anonymous user to http://bob.example.com/bob
, but http://alice.status.net/follow
receives all of her normal cookies and other headers, so alice.status.net
knows she is Alice.
This handler page then uses the postMessage API to communicate safely with bob.example.com to notify it that Alice’s username is alice@alice.status.net.
<html> <body> <script> //This could talk to http://bob.example.com instead of '*' for better security window.parent.window.postMessage('alice@alice.status.net', '*'); </script> </body> </html>
With this last piece in place, Alice is free to subscribe to Bob without remembering arcane credentials, urls, or codes. The two federated websites are able to automatically coordinate how Alice will follow Bob.
Protocol Design
A note on the protocol design: This is a rough prototype to show the design pattern and how it solves the problem. I’ve spent only a few minutes designing the ostatus:// protocol, it is not a standard. Note: There is an existing server to server protocol OStatus 1.0 Draft 1, but this demo client side protocol isn’t a part of that spec currently.
We have to be careful with creating new URI scheme names. We should be thoughtful with protocol designs as we want to make extensible designs that can be given new capabilities without breaking old clients.
There are at least two pieces to play with: The protocol url and then the postMessage interaction. You can design very simple interactions or complex dances back and forth between site A and B via the iframe. Protocols should probably include a version number, etc.
Another design decision is the granularity of protocols: Should we have a new scheme follow://
that handles only one method? Or an ostatus://
scheme that bundles several methods? How many methods should ostatus://
include in it’s initial specification?
This demo is a simple one way communication in the iframe, but could be two way with negotiations and taking protocol versioning into account, much like HTTP is an extensible protocol (content negotiation, very few assumptions, etc).
This demo implements two actions: test and follow. This is enough of the OStatus protocol to kick the tires on the idea, there are other useful actions that would be baked into the real ostatus protocol handler.
UX Problem
A Google search for registerProtocolHandler shows 26,600 results, so it isn’t widely used or discussed currently. Why?
In the video we show what happens if the user doesn’t have an ostatus
protocol handler registered. There is no way to query this ahead of time. You can detect the failure and show some help describing how to get an OStatus enabled service, etc. At the end of this post I’ll sketch out some UX improvements to make this less painful.
It would be nice if there was a navigator.isProtocolRegistered('ostatus')
that returned true if one or more handlers were registered. This would help solve this issue. Update: dmose points out Bug#440620 which suggests a protocolRegistered function.
General Solution
This federated website discover technique really improves the user experience for Firefox 3.5+ users. No other browsers implement registerProtocolHandler, but that’s okay as this is a progressive enhancement. Once they do, the awesome sauce will start flowing for them too. The Chromium project is also working on implementing this feature.
Assuming good UX norms can be built around how to use registerProtocolHandler-then-postMessage, It’s quite possible that this technique can be used to solve the following hard problems:
- Nascar problems
- OpenID
- "Share this" and "Universal Like button"
- Bookmark using my Bookmark service
- Repost to my blogging service
- Tweet this URL with my microstatus service
- Save this recipie to my cookbook app
- Add this contact to my address book
A lot of smart people have done great work in solving this problem. Other solutions that depend on a centralized service, such as Meebo’s XAuth can still be used as fallbacks… Progressive enhancement FTW.
Smoothing out the Gotchas
There is some work to be done around the best user workflow. Below we will use localStorage to remember where we are at in registering a protocol handler so we don’t bug the user over and over for a missing protocol handler.
Here is a sketch at fixing registerProtocolHandler
gotchas. We keep track of the protocol handler by testing it at various stages and caching the answer across page loads. Something much better can be created per website to help ease adoption and explain to the user why they see the protocol dialog.
var isProtocolRegistered = 'unknown'; if ('localStorage' in window && window['localStorage'] !== null) { if ('ostatus_handler_state' in localStorage) { isProtocolRegistered = localStorage['ostatus_handler_state']; } else { // first time... initialize localStorage['ostatus_handler_state'] = isProtocolRegistered; } if ('unknown' == isProtocolRegistered) { if ('navigator' in window && 'registerProtocolHandler' in window.navigator) { // Hack url below would be Status.net JS or PHP emitting the url instead of hardcoding to the user alice navigator.registerProtocolHandler("ostatus", "http://alice.status.net/follow?uri=%s", "OStatus"); localStorage['ostatus_handler_state'] = 'shown'; } } var testProtocol = function() { $(window).bind('message', function(event){ localStorage['ostatus_handler_state'] = 'working'; }); $('body').append("<iframe id='ostatus_test_frame' src='ostatus://alice.status.net/test'></iframe>"); var frame = $('#ostatus_test_frame'); } if ('shown' == localStorage['ostatus_handler_state']) { // Is the handler still working? localStorage['ostatus_handler_state'] = 'optout'; setTimeout(testProtocol, 5000); } else if ('working' == localStorage['ostatus_handler_state']) { // At one time we had a working protocolHandler... is it still working? // This handles the case were user removes handler via browser preferences // We'll want to try to registerProtocolHandler again on next page load localStorage['ostatus_handler_state'] = 'unknown'; setTimeout(testProtocol, 5000); } } }
Your Turn
What do you think? Can we use registerProtocolHandler and postMessage (repo for short) as the new plumbing for federating web applications? If so, these primitives can be the building blocks of your future protocol designs.
Editors Note
A big thanks to korge (Wilson Lee), Paul Osman, and Ryan Snyder for editing and feedback on this post.
Austin King wrote on :
john wrote on :
Fabian wrote on :
Patrick Aljord wrote on :
Brett Zamir wrote on :
Paul Osman wrote on :
Gadget Wisdom Guru wrote on :
Austin King wrote on :
Antimatter15 wrote on :
Brett Zamir wrote on :