RegisterProtocolHandler Enhancing the Federated Web

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.

10 responses

  1. Austin King wrote on :

    Also check out Steve Ivy’s great blog post on a similar idea for a Safari Addon.

    http://www.monkinetic.com/2010/07/webbased-url-handlers.html

  2. john wrote on :

    forgive my ignorance on ostatus, … but isn’t this vulnerable to man-in-the-middle? Couldn’t Ted maliciously register his site as the protocol listener for Alice when she visits his site (run the appropriate js on all visitors)? Alice must then be vigilant, paying attention to her site’s name (and Not Ted’s) in the subscribe pop-up. Some additional social-engineering/data-mining on Ted’s part would make his site-URL sound familiar to Alice in the pop-up there-by not calling attention to itself.

    -john

  3. Fabian wrote on :

    I also want to mention this article an Devmo: https://developer.mozilla.org/en/Web-based_protocol_handlers (I linked this article there too).

  4. Patrick Aljord wrote on :

    @john ostatus is basically oauth, so it’s as secure as oauth can get (which is used by twitter and facebook).

  5. Brett Zamir wrote on :

    Besides, bug 440620 (which would be nice), I’ve made a similar feature request as Bug 539889 and implemented a Firefox extension to handle fallback URIs (or alternative URIs available via right-click) as proposed additional attributes on (though a function would be nice too): and https://addons.mozilla.org/en-US/firefox/addon/162154/

  6. Paul Osman wrote on :

    @Patrick Actually, OStatus doesn’t use the same protocol flow as OAuth. OStatus uses Activity Streams, PubSubHubbub, Salmon and WebFinger / LRDD to do cross site status updating and following. The security part of OStatus is mostly covered by Salmon [1], which uses Magic Signatures [2] (a separate spec) to sign messages with a private key. That’s where the bulk of the security meat is.

    [1] http://salmon-protocol.googlecode.com/svn/trunk/draft-panzer-salmon-00.html
    [2] http://salmon-protocol.googlecode.com/svn/trunk/draft-panzer-magicsig-00.html

  7. Gadget Wisdom Guru wrote on :

    What if you have a personal and a business OStatus account and want to be able to handle them both?

  8. Austin King wrote on :

    @john – This technique is susceptible to a phishing attack. I don’t see Repo having a higher risk profile than other HTTP interactions. Registering a protocol handler requires user action, so it isn’t as bad as a drive-by download or other such attacks.

    @Brett – Awesome work on the UX. Does your Addon inject a protocolRegistered function into the page content?

    Just a reminder that the ostatus:// link starts a client side interaction (not the OStatus 1.0 Draft server to server protocol), so any security issues should be vetted for registerProtocolHandler and postMessage in general… and then the ad hoc protocol I’ve created in the blog post for “following” someone. This work can be tracked in http://status.net/open-source/issues/2473 and work highlighting security issues would be appreciated.

  9. Antimatter15 wrote on :

    The registerprotocolhandler postmessage pattern could also possibly be used as somewhat of a less scalable alternative to the quasi centralized xauth system by checking for protocol handlers for site_0 for postmesage response and increasing the counter until a site doesn’t respond to get a length for inserting or reading.

  10. Brett Zamir wrote on :

    @Austin: No, no injected function….but you can use the href attribute with the javascript: protocol in case the defaultURIs attribute doesn’t work (probably for most people now).