For most sites that accept OpenID today, the user experience is one of two things:
- User is redirected to the OpenID provider, and then redirected back to the original site. This is the most popular one, but it’s a particularly jarring experience for the user.
There has been some discussion lately about how the OpenID experience can work within a popup window. Next week, Facebook is hosting an OpenID design summit to work through some of the issues around cohesive design within a popup. However, one question that I’ve heard several times is “How does the popup work for Facebook Connect? Can it be done for OpenID?”
In theory, yes. In this blog post, I’ll walk through an approach for how the existing OpenID 2.0 spec can be used to do the entire exchange within a browser popup, so that the page doesn’t even have to refresh. Once the user is logged in, she can just keep doing what she was doing. At the moment this is just theoretical, but I will put up sample code and a demo once I get it all working.
Update: Brian Ellin implemented this idea in, like, an hour. His sample code implements a slightly simpler version of the technique described here. It’s really quite good.
How it works
window.open to initiate the transaction.
The first part of any OpenID transaction is discovery - that is, given a URL like “yahoo.com”, where do I go to actually log the user in? The RP also needs to establish a secure association, so that it can verify the signature on the response for security.
We open the popup onto a helper file located in the domain of our site. That helper file does the appropriate discovery, and then does a redirect to the OpenID provider, which remains within the browser popup. The OpenID provider walks the user through the steps to log in. It doesn’t even know it’s in a popup - as far as the provider is concerned, this is a full page (Although there are some discussions about how to let that it’s in a popup, nothing has made it into the spec yet).
The OpenID provider within the popup looks something like this:
Finally, the provider redirects back to the
Here’s an example of a return_to cross-domain url:
What about performance? This flow involves the following HTTP requests:
- The initial request for the RP page.
- The load of the helper page in the popup (which does background discovery)
- The redirect to the OP
- Probably, at least one form submit within the OP. (The user enters their name and password, and submits)
- The load of the reciever file when the OP redirects back
- The final Ajax call to the RP server to validate the signature
At least two of these HTTP requests can be optimized away:
- Load of the receiver page. The client needs to redirect to a page that lives on the RP domain- but that doesn’t mean that it has to load that page from the server. If the RP serves the cross domain receiver as static HTML with long cache headers, then the user’s browser will cache the page. As long as the query string doesn’t change, the browser won’t need to fetch the reciever again.
But how can the query string not change? After all, the OpenID parameters need to be sent back in the query string, right?
The way around this is to send the parameters back not in the query string but the fragment. So the return would look like:
Because the part before the fragment doesn’t change, this file never needs to be reloaded, and this HTTP request is saved.
The techniques laid out here can help the OpenID user experience reach the same level of fluidity as that achieved by Facebook Connect. Now it just remains to get some working code, and put it in practice!