The Role of APIs in the Future of Search (on SEOmoz)

People talk a lot about APIs in the SEO industry (me especially) – the tools you can build with them, the competitive analysis data you can access, the reports you can automate. However, we tend not to discuss the wider picture, the thousands of APIs out there for other things, and, most importantly, the profound effect that APIs are going to have on the web, and thus the SEO industry, in the coming decade.

How Authorship (and Google+) Will Change Linkbuilding (on SEOmoz)

Google’s relationship with links has changed over the last 15 years – it started out as a love affair but nowadays the Facebook status would probably read: “It’s Complicated”. I think Google are beginning to suffer from trust issues, brought about by well over a decade of the SEO community manipulating the link graph. In this post I’m going to lay out how I think Authorship, and Google+ are one of the ways that Google are trying to remedy this situation.

Monitor Which Social Networks Your Visitors are Logged Into With Google Analytics (on SEOmoz)

Recently Mat Clayton from Mixcloud provided a great snippet of Javascript that could be used to record whether visitors to your site were logged into Facebook or not. I extend that idea to present similar code for Twitter and Google+ and then wrap it all up in Google Analytics goodness. Using this code you can monitor which social networks your website visitors are logged into.

Detect if visitors are logged into Twitter, Facebook or Google+


Example output – try it here.

The quick version: I’ve found a way to abuse the login mechanism for both Twitter and Google to detect whether a user is logged in to that service. Facebook provides an API for this. So I provide a cross-browser javascript template that works for all 3 networks. If you want to get straight to the code jump to the implementation section or check out the Social Network Login Status Detector Demo.

Introduction

I was interested in seeing whether it would be possible to track which social networks a website visitor is logged into at the time of their visit; it could be cool for selecting which social media buttons you show them, what sort of marketing you do to them, or simply to evaluate whether you should be participating more on a certain social network. I was interested in Facebook, Twitter and Google+; as an SEO I was also interested in whether people were logged into a general Google account so I could compare which percentage of those had a Google+ account.

A quick search turned up an interesting post from Mike Cardwell who had a method for doing this for Facebook, Twitter and Gmail, but unfortunately it didn’t work in Internet Explorer. Secondly, I knew there was a better method than Mike’s for Facebook, which I’d seen presented by Mat Clayton of Mixcloud; he uses Facebook’s API to do the same thing (see slide 15). Mat’s method works great across browsers, so that solved the Facebook side of this.

Finding a way in to Twitter and Google+

Wat I needed was a method for detecting whether a visitor to my site was logged in to Twitter, Google and more specifically Google+.

Thanks to abraham from Hacker News I discovered that Twitter has an undocumented endpoint that simply returns true or false for whether the current user is logged in! It is very simple:

  1. <script>
  2.     function twitterLoginStatus(state) {
  3.       alert(state);
  4.     }
  5. </script>
  6. <script src='https://api.twitter.com/sessions/present.js?callback=twitterLoginStatus'></script>

However, due to boring technical details concerning MIME types this code doesn’t work on IE9, which (unfortunately) for many purposes makes it less than ideal.

Browsers nowadays are very sensitive to cross site requests and the all to common exploits that abuse them, and so unless the 3rd party site plans to allow it using javascript for this is probably going to be difficult. The other great way to make cross domain requests is with image tags.

Tricking login mechanisms

I came up with the theory that I needed to try to access and image on Twitter/Google’s sites that would only be available to users when they are logged in. Using javascript I could detect whether the image loaded or not and thus determine whether the user was loggedin. However, these are obviously going to be few and far between (image assets are often static and so on CDNs and/or not protected in such a manner), if they exist at all (I didn’t find any), so I was back to square one. I needed a protected area of the site, but needed the file contents to be an image.

My winning moment was realising that some naive login systems might be open to abuse for exactly this purpose. It is often the case that you try to access a specific page on a site, lets say the “Upload a photo” page but you need to be logged in to do so. If you are not logged into the site in question, when you visit the URL the page redirects to the Login page to authenticate you are who you say you are; however the site wants to be helpful and send you to the page you were looking for so they keep a track of that target page in the URL as a parameter and then helpfully redirect you to that page after login is complete.

What happens if you visit the login page with a ‘redirect on login’ parameter and you are already logged in? When implemented in a naive fashion you are simply immediately redirected to the page specified in the parameter. Some sites limit that parameter to being another page on the same domain, but we’ll see that doesn’t help for this trick.

This mechanism is open to abuse in exactly the way I needed; I could set the ‘redirect on login’ page to be an image file on the same domain. For example:

  1. <img src="https://twitter.com/login?redirect_after_login=%2Fimages%2Fspinner.gif" />

In this example, if I am logged in Twitter is kind enough to 302 redirect me to the image file I specified, but if I am not logged in I am show the login page. It turns out that both Twitter and Google’s login mechanisms are susceptible to exactly this trick. It seems LinkedIn and Tumblr are currently immune to this, though I didn’t dig too deep so there might be another redirect URL for them.

Putting it all together

From this point on it was quite easy to hack together some javascript; just stick this code in the <head></head> section of your page:

  1.  <script type="text/javascript">
  2.    function show_login_status(network, status)
  3.    {
  4.     if (status)
  5.     {
  6.      alert("Logged in to " + network);
  7.     }else{
  8.      alert("Not logged in to " + network);
  9.     }
  10.    }
  11.  </script>

Then, anywhere in your code that seems like a nice place stick this HTML:

  1. <img style="display:none;"
  2. onload="show_login_status('Google', true)"
  3. onerror="show_login_status('Google', false)"
  4. src="https://accounts.google.com/CheckCookie?continue=https%3A%2F%2Fwww.google.com%2Fintl%2Fen%2Fimages%2Flogos%2Faccounts_logo.png&followup=https%3A%2F%2Fwww.google.com%2Fintl%2Fen%2Fimages%2Flogos%2Faccounts_logo.png&chtml=LoginDoneHtml&checkedDomains=youtube&checkConnection=youtube%3A291%3A1"
  5. />
  6.  
  7. <img style="display:none;"
  8. onload="show_login_status('GooglePlus', true)"
  9. onerror="show_login_status('GooglePlus', false)"
  10. src="https://plus.google.com/up/?continue=https://www.google.com/intl/en/images/logos/accounts_logo.png&type=st&gpsrc=ogpy0"
  11. />
  12.  
  13. <img style="display:none;" src="https://twitter.com/login?redirect_after_login=%2Fimages%2Fspinner.gif" onload="show_login_status('Twitter', true)" onerror="show_login_status('Twitter', false)" />
  14.  
  15. <div id="fb-root"></div>

Finally, somewhere after that HTML stick this Javascript:

  1. <script>
  2.  window.fbAsyncInit = function(){
  3.   FB.init({ appId:'xxxxxxxxxxxx', status:true,  cookie:true, xfbml:true});
  4.   FB.getLoginStatus(function(response){
  5.    if (response.status != "unknown")
  6.    {
  7.     show_login_status("Facebook", true);
  8.    }else{
  9.     show_login_status("Facebook", false);
  10.    }
  11.   });
  12.  };
  13.  // Load the SDK Asynchronously
  14.  (function(d){
  15.   var js, id = 'facebook-jssdk'; if (d.getElementById(id)) {return;}
  16.   js = d.createElement('script'); js.id = id; js.async = true;
  17.   js.src = "//connect.facebook.net/en_US/all.js";
  18.   d.getElementsByTagName('head')[0].appendChild(js);
  19.  }(document));
  20. </script>

You will need to replace xxxxxxxxx above with the appID for an app created for your domain; if you don’t have one you can create one in about 60 seconds. Simply visit https://developers.facebook.com/apps whilst logged in to Facebook, and click “Create New App”. You will be prompted for a “Display Name”, and you can enter any old dummy text here and press Continue. On the next page it is only necessary to fill out the “App Domain” and the “Website” with the URL of the domain you want to use this code on. Do that, save changes and grab the “App ID” from the top of the page and enter it in the code above.

You should be all set! Now you can change your alert() functions to do whatever you want based on the login status of the user. See a demo of it in action Social Network Login Status Detector Demo.

Wrap up

In my testing this worked on a range of versions of Firefox and Chrome, IE versions 7 and up, Safari and Opera. It may be that these loopholes get fixed, but in the meantime I implore you to only use this in nice ways. There is an argument that a 3rd party even knowing what other sites you are logged into is a breach of your privacy, and I can certainly see why some people would feel like that (especially if this was scaled up to more personal sites that you might be logged into). If you want to prevent this then for Firefox you can try RequestPolicy or NoScript. For Chrome you can give ScriptNo a shot. On IE you can try giving Firefox or Chrome a try. 😉

However, I do also think that this sort of thing can be used in good ways – serving only a subset of social buttons to your users, or determining whether you should be providing support on a given social platform etc. If anyone has any nice suggestions for other ways you could use (nicely) use this, I’d love to hear.

SEOmoz API Signed Authentication with Javascript

Whilst creating my International SEO Backlink Analysis Tool, I wanted a way for people to use their SEOmoz API key in the same secure fashion as they would for their homebrew tools. That meant that their Secret Key shouldn’t be transmitted over the wire, but instead only a signed hash. You can read the docs on the SEOmoz API authentication, if you’re not familiar.

I created a simple Javascript function which generates the signed authentication hash. It’s very easy to use:

  1. var SEOmozCredentials =  getSEOmozCredentials(accessid, secret);
  2. var timeStamp = SEOmozCredentials['theTimeStamp'];
  3. var signature = SEOmozCredentials['signature'];

The parameters you must pass are the SEOmoz Access ID and the Secret Key. Once you have the timeStamp and signature, in addition to the Access ID you can easily form the request on the server side; here is some example PHP code:

  1. $credentials  = "AccessID=" . $accessID . "&amp;Expires=" . $timeStamp . "&amp;Signature=" . urlencode($signature);
  2. $apiURL = "http://lsapi.seomoz.com/linkscape/links/" . urlencode($url) . "?" . $credentials . "&amp;SourceCols=5&amp;TargetCols=0&amp;Filter=external+follow&amp;Sort=page_authority&amp;Scope=page_to_page&amp;Limit=1000";

Make sure you don’t transmit the Secret Key. You can grab the standalone Javascript file here: SEOmoz API Javascript.

You may want to consider, as an alternative to using JS in this way doing full SEOmoz Application Authorization.