How to accept OpenID in a popup without leaving the page

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.
  • User is given a Javascript browser popup, but when the popup returns, it still refreshes the whole page. I haven’t actually seen this in the wild, but I’ve heard it discussed.

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.

See it in action here.

How it works

Okay, so suppose the user is on a page, and they see a “Sign in with OpenID” button. I’ll assume they have some way of choosing their provider. In the image below, I use the “Sign in with Yahoo” button. The user clicks the button, which triggers a Javascript handler. That handler sets up a callback (I’ll get to that later) and then calls 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 openid.return_to url. Remember in the first step, before the popup was even opened, the Javascript handler set up a callback? Well, the return_to url is a specially encoded cross domain url. It encodes information about that callback so that it can be decoded on the reply.

Here’s an example of a return_to cross-domain url:


http://open.sociallipstick.com/openid/xd_receiver.htm#fname=_opener
&%7B%22t%22%3A3%2C%22h%22%3A%22openIDresponse%22%2C%22sid%22%3A%220.672%22%7D

The cross-domain URL reads the response parameters, and just passes them directly to the parent page using Javascript. Because the cross-domain receiver and the parent page are on the same domain, the communication can proceed.

What next? The parent document now has all the OpenID parameters, but it’s in Javascript. The last step is to verify those parameters. The RP can make an Ajax call to a helper script, which looks up the association and verifies the signature. It can then perform any logging in, and pass back a success variable to the Javascript to let it know that everything went well. Of course, if the Javascript wants it can then go ahead and refresh the page, but if the user is in the middle of something, it can wait until the user is done to do so.

Performance

What about performance? This flow involves the following HTTP requests:

  1. The initial request for the RP page.
  2. The load of the helper page in the popup (which does background discovery)
  3. The redirect to the OP
  4. Probably, at least one form submit within the OP. (The user enters their name and password, and submits)
  5. The load of the reciever file when the OP redirects back
  6. 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 helper popup. In the common case, users will be using an OpenID provider that’s been used before. For example, if someone clicks the “Yahoo” button, then the RP doesn’t need to do discovery on Yahoo.com again. In fact, the RP should cache both the server endpoint and the secure association, and make them available in Javascript. If that were the case, then the popup could open directly onto the OP site, and skip this one.
  • 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:

    http://sociallipstick.com/receiver.htm#handler_info&openid.ns=…..

    Because the part before the fragment doesn’t change, this file never needs to be reloaded, and this HTTP request is saved.

Conclusion

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!


31 Responses to “How to accept OpenID in a popup without leaving the page”

  • Joseph Smarr Says:

    Great work, this sounds very promising! But I thought you said OPs in general won’t keep/honor the fragment part of a return_to URL? Did you manage to work around that? Can a server issue a 302 to a URL that includes a fragment, and will the browser do the right thing? Also, while it’s true you can cache discovery/association info, you still need to a) handle the case where it’s expired since you last redirected to the OP, and b) construct the checkid_setup URL, so my guess is in practice you’ll still always need that helper.php hit in the middle when redirecting to the OP in the popup. But otherwise I totally agree this is a good way to handle OpenID in a popup, which I do expect will become quite common in the near future. ;)

  • Brian Kissel Says:

    Check out the user experience at http://uservoice.com/session/new Login with AOL, logout, then login again with AOL. This “one click” login doesn’t require a redirect as long as you already have an active session at AOL. Is this the kind of experience you’re advocating?

  • Facebook User Says:

    The other thing I would add is to make use of the checkid_immediate mode to further improve the UX. The page doesn’t even need to open the popup if the user has previously given consent to always login automatically. The big problem is the lack of a standard size (or a way to request) of the login/authorization UI displayed in the popup window. Opening up a popup as big (or even bigger) as the current page is a bad UX too as it doesn’t provide the same contextual experience as FBConnect.

    Btw - you could probably (re)use the code in Dojo library built for IFrame based cross domain HTTP requests (which is essentially same as what you described above but using an Iframe instead of a popup) to package the request data that has to be passed over to the JS handler in the parent page.

  • Luke Shepard Says:

    Hey Joseph- thanks for the feedback.

    The spec is a little ambiguous about what happens to a return_to url that contains a fragment. Should it insert query parameters into the proper place, preserving the hash? Or should it slap it on the end? The big providers right now - Microsoft, Yahoo, and Google - all just slap it on the end, so it works with them.

    As for the optimization, I suppose you would need helper.php, at least some of the time. If the cache fails or there’s an error with an association, then yeah, the server should refresh it. But in general I think it will be good for most hits - after all an association may be good for at least a few hours at a minimum, and in that time a good RP might serve millions of requests. So optimizing away that HTTP hit could still be a big deal.

  • Luke Shepard Says:

    @Brian, I tried out uservoice but was still redirected to AOL even after logging out several times. AOL kept asking me to send over SREG info too, which is pretty annoying (I kept saying no).

    One thing I didn’t mention (but will go update) is that the method described above could easily be changed to do hidden detection. Simply substitute iframe for popup, and checkid_immediate for checkid_setup, and you have a completely hidden background check for login status that doesn’t do any redirects.

    Ultimately it will be faster to do a checkid_immediate via full-page redirects, since you don’t need to wait for Javascript to load. But I think that should only be done for providers that the RP *knows* will redirect back immediately. A big risk I see is an OP that “claims” immediate mode, but doesn’t send the user back promptly. I’m not sure how reliable immediate mode is on these providers yet (do you have any insight into that)?

  • Luke Shepard Says:

    @Praveen-

    I found that a width of 790 and height of 580 worked reasonably well for existing UI for top providers- Microsoft, Yahoo, AOL, Google. Many websites need to be able to display in a small window (like on a small laptop) anyway, so it’s not terrible. But yeah, it’s an issue, and we will also be discussing the size issue next week at the OpenID Design Summit.

    I have heard of the Dojo XD library but have not used it. But yes, it’s exactly the kind of thing that should be used. The code I’m working with is based on the cross domain library Wei Zhu wrote for Facebook Connect. I’ll publish in a bit (once I’ve worked out the kinks).

  • Facebook User Says:

    Luke, this is an awesome overview! Your post inspired me to try and get this experience working, so I cooked up something over at http://openid-demo.appspot.com/

    After starting, I realized that it is possible to create a similar experience using a different technique that doesn’t utilize cross domain communication. Here’s how it works:

    1) The login page pops up a browser window to openid-demo.appspot.com/begin_openid which does discovery and associates with the OpenID provider (this plays the role of your helper.php). The popup is then redirected to the OpenID provider for user auth. The user authenticates at their OP, and then is redirected back to the return_to.

    2) The return_to page handled in the popup window simply extracts the query parameters off of the URL and then calls window.opener.handleOpenIDResponse(openid_params). This javascript call works because even though we had redirected away to the OP, we are back on the same domain as the opener window. The javascript then calls window.close() to close itself.

    3) The handleOpenIDResponse javascript callback in the main page makes an AJAX request back up to the server with the OpenID parameters. The server then uses the params to verify the OpenID signature and finish the OpenID request, potentially signing the user in to the site and then sending an appropriate response back to the page.

    Notes:

    - The experience is kind of a bummer if have already signed in to and “trusted” your OP. For example, sign in to the demo using google and check the “remember” box. Then do it again. The popup opends and closes very quickly, which is probably not the best user experience.
    Any thoughts on this approach?

    - This flow can be very easily adopted to existing OpenID implementations by adding return_to popup handler and minimal amount of javascript. The existing OpenID code can pretty much be re-used with very little alteration.

    Thoughts on this approach?

  • luke Says:

    Brian, thanks for doing the demo. Very slick. Always helps to communicate with code.

    The technique you’re using is exactly what I was talking about - you’re just not using a library because it’s a simpler use case than the general XD comm. Looks great.

    > The return_to page handled in the popup window simply extracts the query parameters off of the URL
    > and then calls window.opener.handleOpenIDResponse(openid_params).

    Yep; you’re right, it works fine without the fragment stuff. The purpose of the fragment is to just save that HTTP trip.

    > The popup opens and closes very quickly, which is probably not the best user experience.

    We have this same issue with Connect. I think it can be solved in a few ways (some described by Joseph above):

    1. If you have a cookie on the computer saying what provider, then you can issue a checkid_immediate request ahead of time using either an iframe or a full-page redirect.

    2. If you have no cookie, then you can *still* issue a checkid_immediate request for all major providers- at least the ones you offer links for. You would do this in the background, with iframes. If any of them hit, then great!

    That’s good news that it can be adopted to existing implementations. Can’t wait to see the code.

    See you Tuesday?

  • Allen Tom Says:

    Luke, very interesting overview, and I’d like to see if we can reuse this technique to improve the OpenID’s checkid_immediate interface.

    One concern that I have about the xd_reciever.htm technique is that it might potentially expose the RP’s site to XSRF or XSS attacks. Does xd_reciever.htm ensure that only data passed from the OP is injected back to the RP’s parent window?

  • luke Says:

    The RP should treat any incoming data as suspicious, whether in the hash or the url, or via a popup or whatever. In this case the RP is just slurping the data en masse from the return_to, and then chucking it into an Ajax request to the server. The server should properly sanitize it before sending it back in the response. So I think we’re okay from an XSRF perspective.

    One improvement to the checkid_immediate interface that I’d like to see is the option to have a third state - neither completely logged out nor completely authorized, but when the user is logged in but not authorized. An RP could poll providers in the background then, and emphasize buttons for the providers that the user is currently logged in with.

  • luke Says:

    Oh, I meant we’re okay from an XSS perspective. The XSRF should be helped because of the nonce in the OpenID protocol that prevents replay attacks, right?

  • Allen Tom Says:

    I’m not so much worried about XSS/XSRF for OpenID requests, I’m just wondering about the scenario where a user has their browser open to mail.portal.com, and portal.com has an xd_reciever file installed at portal.com/xd_receiver.

    The user then has another browser window pointed at evil.com, which then silently sends requests to portal.com/xd_reciever. I’d just like to make sure that there’s no way for evil.com to get access to the DOM or JS running on portal.com by exploiting any holes in xd_reciever. I guess that as long as there are no evals() on the data that’s passed to xd_receiver, then we’re probably OK.

  • Wei Zhu Says:

    Hey Luke,

    Nice write-up. And I just want to say that I like the theme and layout of your blog. It feels nice and clean.

  • OpenID » Blog Archive » Facebook joins OpenID Foundation Board with a commitment to better user experience Says:

    [...] Given the popularity and positive user experience of Facebook Connect, we look forward to Facebook working within the community to improve OpenID’s usability and reach. As a first step, Facebook will be hosting a design summit next week at their campus in Palo Alto which follows a similar summit on user experience hosted at Yahoo! last year. The summit will convene some of the top designers from Facebook, the DiSo Project, Google, JanRain, MySpace, Six Apart and Yahoo!, focusing on how existing OpenID implementations could support an experience similar to Facebook Connect. [...]

  • Facebook Joins OpenID Foundation Board Says:

    [...] The move is a big bolster to the OpenID movement. Facebook has been participating in OpenID discussions throughout the development of Facebook Connect. Facebook’s Julie Zhou presented at the UX Summit held at Yahoo last October. Facebook will host a similar OpenID design summit at its Palo Alto headquarters next week, which will include designers from “Facebook, the DiSo Project, Google, JanRain, MySpace, Six Apart and Yahoo!, focusing on how existing OpenID implementations could support an experience similar to Facebook Connect.” [...]

  • Facebook User Says:

    This is some good work - thanks for taking the time to illustrate it so well. And here I was thinking that OpenID [implementors] were trying to make life difficult with crazy complex URLs and the like…

    Sam
    http://samj.net

  • Welcoming Facebook to the OpenID Foundation | FactoryCity Says:

    [...] To that end, Facebook will be hosting the second User Experience Summit for OpenID on February 10th. The goal is to convene some of the best designers that leading internet companies can muster, and bring them together to develop a series of guidelines, best practices, iterations, and interfaces for making OpenID not just suck less, but become a great experience (in same vein as the hybrid OpenID/OAuth flow that we saw from Plaxo and Google last week, and in line with Luke Shepard’s proposals for an OpenID popup). [...]

  • Facebook User Says:

    Luke, I posted an update on the demo page that includes the relevant code bits. Looking forward working out the kinks on Tuesday :)

  • Facebook Joins OpenID Foundation Board | Webtrendblog.com Says:

    [...] The move is a big bolster to the OpenID movement, which hopes to create an “open, decentralized, free” framework for user identity across the web. The OpenID Foundation was formed in 2007 to help promote the OpenID technologies and community, and is currently governed by 7 community elected board members and 7 corporate board members. Facebook engineer Luke Shephard, a “huge internal advocate for OpenID,” will serve as Facebook’s representative. Facebook has been participating in OpenID discussions throughout the development of Facebook Connect. Facebook’s Julie Zhou presented at the UX Summit held at Yahoo last October. Facebook will host a similar OpenID design summit at its Palo Alto headquarters next week, which will include designers from “Facebook, the DiSo Project, Google, JanRain, MySpace, Six Apart and Yahoo!, focusing on how existing OpenID implementations could support an experience similar to Facebook Connect.” [...]

  • Arjun Banker Says:

    great post luke, really love your blog design too :-)

  • Cross Site Scripting » Blog Archive » Social Lipstick » Blog Archive » How To Accept Openid in a Popup … Says:

    [...] I’m not so much worried about XSS /XSRF for OpenID requests, I’m just wondering about the scenario where a user has their browser open to mail.portal.com, and portal.com has an xd_reciever file installed at …Next Page [...]

  • Joseph Zinkann Says:

    I think the best approach is to create an iframe to receive the data from a pop-up. I am concerned about the evil.com domains. Thanks for the article.
    Joe

  • People at Facebook | Spin Valley Post Says:

    [...] House on best practices for the President’s website and even an explanation as to how the OpenID experience can work within pop-up windows. The aggregator will pick up any new blog posts by our [...]

  • PR for facebook | Social Networking Says:

    [...] House on best practices for the President’s website and even an explanation as to how the OpenID experience can work within pop-up [...]

  • Social Lipstick » Blog Archive » Making OpenID more useful: let’s detect logged-in state Says:

    [...] mode that most people are familiar with, and much of the user experience discussion (including my previous post about the popup UI) has focused on [...]

  • David John Boden Says:

    Hi, just a quick note to say that I’m building this sort of client-only OpenID RP implementation into the http://code.google.com/p/openid-selector/ project. The discussion document for what’s going on is at http://code.google.com/p/openid-selector/wiki/JavascriptOpenIDClient and there’s a demo in the early stages of working at http://openid-selector.googlecode.com/svn/trunk/demoOpenIDClient.html

    As long as the client has knowledge of the OpenID endpoint that it needs to deal with, there’s no need for any sort of Cross Site Scripting (XSS). The fact that the OpenID Provider responds by HTTP forwarding back to your domain solves all those problems. The source is available at http://openid-selector.googlecode.com/svn/trunk/ so please do take a look if you have a moment. I’m keeping the openid selector, a graphical component to pick an OpenID URL, separate from the OpenID client implementation. Many users won’t want to use an Javascript OpenID RP implementation so I don’t want to pollute the existing functionality.

  • videos sex Says:

    sex videos…

    effects of a prozac overdose. weight loss after prozac. overdose on prozac. free amateur home sex videos. xxx sex videos. indian sex videos. …

  • Steven Roger Says:

    HI, If you have any source code then please put it. Waiting to see it

  • JESSIE Says:


    PillSpot.org. Canadian Health&Care.Special Internet Prices.No prescription online pharmacy.Best quality drugs. No prescription drugs. Buy drugs online

    Buy:Cialis.Super Active ED Pack.Viagra Professional.Cialis Super Active+.VPXL.Cialis Professional.Viagra.Soma.Propecia.Viagra Super Active+.Viagra Super Force.Tramadol.Viagra Soft Tabs.Zithromax.Cialis Soft Tabs.Levitra.Maxaman….

  • ALLAN Says:


    Pillspot.org. Canadian Health&Care.Special Internet Prices.Best quality drugs.No prescription online pharmacy. Online Pharmacy. Buy pills online

    Buy:Nymphomax.Lasix.Female Pink Viagra.Cozaar.Female Cialis.Buspar.Prozac.Ventolin.Zocor.Zetia.Seroquel.Wellbutrin SR.Acomplia.SleepWell.Benicar.Amoxicillin.Lipitor.Lipothin.Advair.Aricept….

Leave a Reply

You must be logged in to post a comment.