A modern webdev horror story – iFramed content with IE and Safari

Written by Alex Wolkov

This blogpost originally appeared in the Fundbox labs blog.

TL;DR

Let me tell you a modern web developer horror story.

You’ve been working on a feature for months. This feature is a collaboration effort of two companies, integrating via API and some iFramed content. You’ve tested most possible scenarios. Both sides have QA teams running these scenarios again and again before launch. Everything seems fine and you’re happy! Then someone from the other team mentions something in the unified slack channel; “I can’t seem to login with Internet Explorer”.

Now, as an experienced developer, you know that this isn’t possible. You’ve tested this site on IE9, IE10 and IE11! This can’t be right… they must be mistaken. My feature is flawless, you say. Well, if you’re using iFrames and embedding your site in another site’s domain, this is not only possible, but it will happen!

If you arrived here looking for a solution for cookies not being saved inside an iFrame, feel free to skip to the end.

Some background

The feature in question is, allowing businesses from FreshBooks to register and clear their invoices straight from their FreshBooks dashboard. If you’re interested in how this looks, head to the FreshBooks blog. In order to make this functionality, we decided to build a Hybrid API. One part is the backend API, to let FreshBooks query our system and check if the user in question is already registered with Fundbox. The other part is an iFrame front-end API, to let FreshBooks show a modal inside their dashboard which depends on the backend APIs results, to either show a new user sign up page, or a log-in page for an existing Fundbox user. I won’t go into details, but this integration made us do some thinking, and we’ve come up with some creative solutions which deserve a blog post for themselves. But I’m here to tell you a horror story, remember?

Launches are fun when they go smoothly

Launching a feature which involves two development teams from different continents and time zones is tricky. When we finally got everyone at their workplace and started working towards the launch, everything seemed to go fine. Some small bugs crept up, like they always do. Some we fixed on the spot, while for others we decided that they’re not show-stoppers and moved them to after launch. Then, the dreaded words were heard in the slack channel. Something doesn’t work in Internet Explorer…

“I can’t seem to login with Internet Explorer”

This can’t be right, right? We’ve tested with IE, albeit not in production, but we have several staging environments and we’ve tested, and well, it works. Ok, so we test again. I fire up the lovely SauceLabs interface, choose a windows machine with IE 11, and try to login via the iFrame. It works! HA! see guys? I told you it works, something is wrong with your machines…

The QA guys from across the ocean check again on a native IE installation, still no dice. Can’t seem to login, OR register! We check again, this time in a virtual machine, and it indeed doesn’t work. We bring a PC with a native IE – doesn’t work as well. 0_o, it’s 9PM and it’s launch day. This can’t be good…

Debugging iFrames is a fun time that everyone enjoys.

Baffled by the fact that some IE installations work and some don’t, we tried a BUNCH of different scenarios. Antivirus, malware, different networks, WiFi settings. Nothing seems to be the culprit.

Then something pushes me to check out the security settings of the misbehaving IE installation. I lowered all of them to their lowest settings, and everything is peachy, no bug. hmmmmmm

After understanding exactly why the login/register forms didn’t work, we arrived at the fact that the AJAX requests weren’t even leaving the browser. With some additional digging into the code, we placed the the blame on a small piece of code which makes sure that cookies can be saved before even sending the AJAX request. Then we tried to login with this exact URL, but this time in a tab, not an iFrame on another domain and… BOOM. Everything is silky smooth!

The question now is, why then can’t we save cookies inside the iFrame on another domain?

Then the slack channel rings with some more info about this bug…

Safari has the same bug…

How? Safari is a modern browser, unlike IE, right? I mean, usually I don’t even bother checking it, as it’s very modern and cool, and stuff just works there. Also, it has barely any usage percentage. But ok, Safari shows the same behavior, so I can debug this issue natively and much more quickly on my Mac. If the symptoms are the same, it must be the same bug, hence the same solution, right? WRONG! But we’ll get to that.

What the hell is p3p?

After some googling, we arrive at some blog posts that describe the same symptoms, and offer a solution. Let me walk you through it.

There’s a w3c standard called P3P, from 2002, that proposes a way for sites to describe how they store and contain your data over cookies. This standard is not a standard per se, as no browsers have implemented it, mozilla even had this at some point and removed it. No browsers, except of course, INTERNET GORRAM EXPLORER. Now, ok, this sounds not half bad, I mean, if a site is honest about it’s data storing over cookies, and your security settings don’t allow for this, your browser must do the responsible thing and let you know it blocked the cookies, only of course Internet Explorer has a half-assed implementation of this, meaning they will only block the cookies. Without letting you know. Just fail quietly. Jee, thanx!

The solution seemed easy enough

After some digging into the standard, and some helpful Stack Overflow posts, we arrive at a proposed solution. All we have to do, is add an xml file, to a path called “known location” which will describe to the browser, our sites cookie usage policies. A word of caution though, these policies are coded in a VERY weird and unreadable way, something like this

And the xml file should be served from a static location, /w3c/p3p.xml and should looks something like this:

If that’s not enough (this didn’t solve the problem btw), you have to add a header, to all requests on your site, that’s called “compact policy” that will have the same policies statements, and will link to said xml. Because having it in the same url on all sites (what’s called a known location) isn’t enough. The header should look something like this:

Ok, we got the solution, we added a header, with the policies we just found on Stack Overflow. Now what? Now comes the part where we realize, that all those policies are legally binding, and if you put the wrong ones, somebody might sue you, as they have real world meaning. OMG! So how do we find out the right ones? Well, there’s some apps you can download, most of them are antiques and costs money, that will let you edit them. One free one we found is IBMs p3p editor.

Luckily, there’s another solution

Ok, did I mention that IE half assed this implementation already? I have! And I will again. Turns out, that dear IE doesn’t really care what you put in that header, as long as it’s there. Google and Facebook have this header and the contents of it just states that they don’t support it. Because it’s an unfinished standard. And is outdated. And is stupid! So our solution now looks like this:

Yes, we declare our p3p policies as a potato, and explorer then let’s us save the third party cookies. geeez. This solution, also isn’t technically “Fraudulent” but simply is invalid. More on that here

Unfortunately, that wasn’t all of it

When we tested this scenario, we discovered that it didn’t work. Because we assumed that having the same bug, means the same solution for both IE and Safari. It doesn’t. Safari doesn’t care about stupid outdated unsupported standards. What safari does care about, is whether you have visited that site before. If you have, they will allow it to set cookies in an iFrame, if you haven’t, well then, bad luck.

This realization came to us after spending about 2 hours, now VERY late at night, trying to test p3p headers in Safari…. And only after both teams sat down to talk the solutions over on Skype, and the FreshBooks team told us that they research came up with the fact that Safari dropped the p3p support somewhere in version 5.1, sigh.

A solution for Safari

What quickly became understood, that Safari will allow cookies, if the user has previously visited the site not in an iFrame, and that site has placed cookies. Which is good right? I mean, Fundbox users have at least been once on our main domain, fundbox.com. This is BTW why we didn’t find this bug right away. We all had previously visited Fundbox.com on all our browsers. Alas, the new users, those who will use the feature, will have never been to Fundbox.com, as the whole point of this integration, is to let them use Fundbox straight from the FreshBooks interface.

So finally, at 2am, we arrive at the solution.

Check to see if we can set cookies. If we can’t, we then show the users a friendly message, not unlike many you’ve seen on many European site.

Fundbox.com requires cookies. Please click here to continue

After the user clicks the accept button, we then open a small popup, on the right bottom side of the screen, which will load a page on Fundbox.com, which the whole purpose of setting a small cookie, and then message back to the iFrame that cookie has been places. The iFrame will then close the popup, reload itself, and the user can continue with his flow.

This happens very quickly, and because the button is animated with a loading state, the popup opens and closes without the user even noticing this little technique.

Solution overview

In the iFrame we have the code to detect if a cookie can be set, and if not, we remove the “hidden” class of the #cookieless div overlay, which contains the message and the button.

When the user clicks the approve button, we launch a small popup, and set a cookie in that popup

The mouse down part is a small optimization, as safari takes a second or two to check whether the popup window was originated by a user action, or by malicious code. We then add a loading state to the button, and open the smallest popup window, in the right most bottom most section of the screen.

The popup page loads with this small bit of code in the head:

Which sets a cookie, tells the opener (the iFrame) to reload, which works because they are on the same domain, and then closes itself.

Lessons learned

I’ve been doing web development for almost 10 years, and as it turns out, IE doesn’t seem to stop surprising me.

Another thing that I will always remember, is that bugs will happen on production, even though you have tested thoroughly before getting the code to production.

But the horror story does have a happy ending, as we successfully launched this feature to a small subset of our users the very next day, and they love it.

A great amount of respect goes to members of both teams on collaboration and quick response in fixing these very hard to trace bugs!