code.flickr.com

The Joy of Popup Windows

The Joy of Popup Windows

As you’ve probably noticed, we’ve been working on the login process here at Flickr. In an effort to make this flow as painless as possible we’ve moved to a “contextual” login, which is to say, logging in doesn’t require you to leave the page you are looking at. We accomplished this by using a popup window.

We initially considered using an in-page modal dialog box instead of a popup, but quickly dismissed this approach for two security reasons. Firstly, to prevent phishing and cross site attacks it’s very important that login forms are not posted cross domain (Yahoo!’s Membership Platform authentication happens on yahoo.com, not flickr.com). Secondly the URL of the login page should never be hidden from the user.

An in-page modal fell foul of both of these concerns. A more suitable solution would be the popup browser window, once a favorite of advertisers.

Our initial approach

Revisiting our turn of the millennium Javascript for opening windows with the BOM, things looked simple enough: call window.open, make sure the window opened, then set a timeout to see if the window was closed:

function waitForWindowClose() {
	if (web1999Window && web1999Window.closed) {
		// the pop-up was closed, done with auth etc.
		window.location.reload();
	} else {
		// check again in a moment
		setTimeout(waitForWindowClose, 1000);
	}
}//cute function name by Scott Schiller
function webCirca1999(url) {
	try {
		web1999Window = window.open(url,'newWindow','width=640,height=650');
	} catch(e) {
		//handle blocked popup...
	}
	try {
		//this is to check if it really opened
		if (web1999Window.focus) {
			web1999Window.focus();
		}
	} catch(ee) {
		// also handle failure
	}
	waitForWindowClose();
	return false;
}

Unfortunately Yahoo!’s new user signup process closed our popup and opened a new one of their own. This triggered our waitForWindowClose{} function, web1999Window && web1999Window.closed evaluated to true, and the Flickr page refreshed, even though the user hadn’t logged in yet.

The next thing we tried was watching window focus. When the window blurred after clicking on the login popup, we’d know the popup was open, and when focus came back we’d check the user’s login credentials.

But focus tracking also had its problems. Some browsers didn’t always report the window blur event when a popup was open, resulting in inconsistent behavior. It was also possible for the user to accidentally focus back on the window and kill the flow. We tried several ways to track focus, and although behavior got better, it was never 100% reliable. For a feature like login that was obviously a problem.

The Cookie Solution

Ultimately we settled on a solution that forgot about popup tracking and window focus and instead concentrated on checking for the user’s login cookies. The code below polls to see if the login cookie is set, then makes an AJAX request to verify it, then refreshes the Flickr page:

function pollCookie(){

	if(document.cookie.match(sessionRegex)){
		authInProgress = false;
		checkAuth();
	}else{
		Y.later(20, this);
	}

}

This has the benefit of being very responsive, avoids the nightmare of focus tracking, and is low impact (as there’s no need for us to poll the server).

So today we have a reliable contextual login. Our popup system also made it possible to add google and facebook login with less pain than usual.