Update 5/14/2009: this demo now uses the OpenID User Interface Extension. It always sends the extension parameters and opens the window in a 450x500 form factor as recommended by the extension spec.


How it works

In a traditional OpenID relying party interface, the RP implements a UI, and two endpoints on its server for processing OpenID related stuff:

  1. /openid_start - this endpoint accepts an openid_identifier parameter which is then ingested by an OpenID library to find out which provider is used by the identifier and make a secure association with the server.
  2. /openid_finish - the openid.return_to which the OpenID provider redirects the browser to after authentication
The interface you see here introduces three new pieces to the puzzle:
  1. A popup window. Rather than handling /openid_start in the current window, we use javascript to open a new window to this endpoint. Rather than telling the OP to return directly to /openid_finish, we give it a return_to at an intermediate page...
  2. A new endpoint at /popup_return_to which handles the OpenID return_to from the OP. This is an extremely simple page that uses javascript to extract the OpenID parameters from the URL and then call OpenID callback that lives in the opening window. This works because this page and the opening page are on the same domain.
  3. A javascript callback function that lives in this page called handleOpenIDReponse. This is the function called in the previous step. The implementation of this function makes an AJAX call to /openid_finish with the parameters extracted by the /popup_return_to code. The page then updates the interface while the OpenID transaction is verified by the server and then repaints the page according to the response of /openid_finish. Your implementation of /openid_finish would likely sign the user in to your site and then update the interface to a signed-in mode.

Code

The code used to accomplish this experience is relatively simple, so I've included the relevant bits below. Please note that we're using the excellent YUI for the async call to /openid_finish.

This page

<script type="text/javascipt">
function openPopupWindow(openid) {
  document.getElementById('ops').style.display = 'none';
  document.getElementById('bucket').innerHTML = 'Signing you in <img src="/static/spinner.gif"/>';
  window.open('/openid_begin?openid_identifier='+encodeURIComponent(openid), 'openid_popup', 'width=790,height=580');
}

function handleOpenIDResponse(openid_args) {
  document.getElementById('ops').style.display = 'none';
  document.getElementById('bucket').innerHTML = 'Verifying OpenID response';
  YAHOO.util.Connect.asyncRequest('GET', '/openid_finish?'+openid_args,
      {'success': function(r) {
              document.getElementById('bucket').innerHTML = r.responseText; 
         }}); 
}
</script>

<a href="javascript:openPopupWindow('yahoo.com');">
<img src="http://www.yahoo.com/favicon.ico" style="border:none;"/> Sign in with a Yahoo! ID</a>

/popup_return_to

<html><head></head>
<body onload="window.opener.handleOpenIDResponse((window.location+'').split('?')[1]);window.close();">
</body>
</html>

Discussion

This demo was created in response to Luke Shepard's post How to accept OpenID in a popup without leaving the page. Contact Brian Ellin (brian at janrain.com) for more info.