Thursday, 31 May 2012

Cross site HTTP Auth via JavaScript

The best way to authenticate against another website is of course OAuth. But sometimes such an mechanism is not provided. E.g. currently only supports HTTP Auth. Now that is something completely different, you might say (no communication between the two web servers possible). Well, once authenticated via HTTP Auth with any site can embed a HTML5 audio element to play the member streams instead of just the free versions. So to enable a member feature in my Magnatune Player Greattune Player I came up with a hack to authenticate against Magnatune via JavaScript. Actually it's several hacks for different browsers and browser versions.

HTTP Auth behaves very differently in different browsers. All non-WebKit browsers show an Auth dialog box when you embed any content from a site that requires the user to be authenticated. WebKit/Chrome does not do that because users might think this box belongs to the website they are on and not to the third party website. (HTTP Auth sessions can be used for user tracking.) Instead it just fails to embed the resource.

So for Firefox, Opera and Internet Explorer I wrote this code:

I needed to embed some resource from to trigger the HTTP Auth dialog. just contains a decimal number so it is a valid JavaScript and I could embed it with a script tag.

If this script tag's onload fires this means the login was ok. onerror will be fired if the login was not ok because scripts may not be transferred with the HTTP status 401. And even if they could be transferred that way the returned document contains HTML which would raise a JavaScript SyntaxError and will fire the onerror event on the window object.

But only IE>=9 supports the onload and onerror events on script elements, so I just use onreadystatechange for older IEs. Note that in this case I cannot detect if login failed (it will always give the readyState "complete"). But I just don't care for those old IEs.

With this I managed to force the browser's HTTP Auth dialog box and could even detect if login was successful (except for old IEs). But in Chrome/WebKit this did not work. Instead I wrote my own credentials dialog in HTML and used the http://username:password@domain/ syntax to force the browser to login.

But this URL syntax can be used even better for user tracking, because it does not even trigger a dialog box, so this feature was removed in Chrome 19. This meant I had to come up with yet another trick. So no embedded resource shows the HTTP Auth dialog, but when I open a new window on Chrome did display the HTTP Auth overlay. But I don't get any load/error events from such a child window, so what to do? Incidentally Magnatune has a page that accepts an url query parameter and redirects to that url when login was successful. So I wrote this code:

login.html just sets MagnatuneLoginSuccess to true and closes the child window again:

I don't say this is pretty, but it works good enough. See the full source here.

No comments:

Post a Comment