Facebook Callback appends '#_=_' to Return URL

via Facebook's Platform Updates:

Change in Session Redirect Behavior

This week, we started adding a fragment #____=____ to the redirect_uri when this field is left blank. Please ensure that your app can handle this behavior.

To prevent this, set the redirect_uri in your login url request like so: (using Facebook php-sdk)

$facebook->getLoginUrl(array('redirect_uri' => $_SERVER['SCRIPT_URI'],'scope' => 'user_about_me'));

UPDATE

The above is exactly as the documentation says to fix this. However, Facebook's documented solution does not work. Please consider leaving a comment on the Facebook Platform Updates blog post and follow this bug to get a better answer. Until then, add the following to your head tag to resolve this issue:

<script type="text/javascript">
    if (window.location.hash && window.location.hash == '#_=_') {
        window.location.hash = '';
    }
</script>

Or a more detailed alternative (thanks niftylettuce):

<script type="text/javascript">
    if (window.location.hash && window.location.hash == '#_=_') {
        if (window.history && history.pushState) {
            window.history.pushState("", document.title, window.location.pathname);
        } else {
            // Prevent scrolling by storing the page's current scroll offset
            var scroll = {
                top: document.body.scrollTop,
                left: document.body.scrollLeft
            };
            window.location.hash = '';
            // Restore the scroll offset, should be flicker free
            document.body.scrollTop = scroll.top;
            document.body.scrollLeft = scroll.left;
        }
    }
</script>

TL;DR

if (window.location.hash === "#_=_"){
    history.replaceState 
        ? history.replaceState(null, null, window.location.href.split("#")[0])
        : window.location.hash = "";
}

Full version with step by step instructions

// Test for the ugliness.
if (window.location.hash === "#_=_"){

    // Check if the browser supports history.replaceState.
    if (history.replaceState) {

        // Keep the exact URL up to the hash.
        var cleanHref = window.location.href.split("#")[0];

        // Replace the URL in the address bar without messing with the back button.
        history.replaceState(null, null, cleanHref);

    } else {

        // Well, you're on an old browser, we can get rid of the _=_ but not the #.
        window.location.hash = "";

    }

}

Step by step:

  1. We'll only get into the code block if the fragment is #_=_.
  2. Check if the browser supports the HTML5 window.replaceState method.
    1. Clean the URL by splitting on # and taking only the first part.
    2. Tell history to replace the current page state with the clean URL. This modifies the current history entry instead of creating a new one. What this means is the back and forward buttons will work just the way you want. ;-)
  3. If the browser does not support the awesome HTML 5 history methods then just clean up the URL as best you can by setting the hash to empty string. This is a poor fallback because it still leaves a trailing hash (example.com/#) and also it adds a history entry, so the back button will take you back to #_-_.

Learn more about history.replaceState.

Learn more about window.location.


This was implemented by Facebook by design for security reasons. Here's the explanation from Eric Osgood, a Facebook Team member:

This has been marked as 'by design' because it prevents a potential security vulnerability.

Some browsers will append the hash fragment from a URL to the end of a new URL to which they have been redirected (if that new URL does not itself have a hash fragment).

For example if example1.com returns a redirect to example2.com, then a browser going to example1.com#abc will go to example2.com#abc, and the hash fragment content from example1.com would be accessible to a script on example2.com.

Since it is possible to have one auth flow redirect to another, it would be possible to have sensitive auth data from one app accessible to another.

This is mitigated by appending a new hash fragment to the redirect URL to prevent this browser behavior.

If the aesthetics, or client-side behavior, of the resulting URL are of concern, it would be possible to use window.location.hash (or even a server-side redirect of your own) to remove the offending characters.

Source: https://developers.facebook.com/bugs/318390728250352/