Facebook Callback appends '#_=_' to Return URL
Asked Answered
W

23

517

Facebook callback has started appending #_=_ hash underscore to the Return URL

Does anyone know why? What is the solution?

Wrecker answered 20/8, 2011 at 13:5 Comment(39)
I'm also facing with same problem: https://mcmap.net/q/73804/-facebook-callback-appends-39-_-_-39-to-return-url/912011 However, I think it may be related with Firefox release.Pinery
Did anyone find a solution to this problem? The standard facebook connect sdk does not work.Suit
The best we can do until Facebook corrects this bug (or their documentation) is in my answer below. First, set up your login urls in accordance with their documentation. And second, add a quick header javascript hack to avoid this in your code. This will degrade well if Facebook ever fixes this.Erogenous
Irritating FB API bug. It brakes my AJAX navigation :(Wilek
Any idea how facebook appends these characters? Facebook redirects to my handler where I then handle the redirection to the return url, yet the characters are still appended to the url.Stereotropism
developers.facebook.com/bugs/318390728250352 This link might make you laughHudibrastic
@BenFoster I think you'll find if you use Fiddler or similar that when FB redurects to your handler, the #_=_ is in place, then even though you do a Response.Redirect to where you actually want to do, the browser maintains the hash, which is why it's only the client-side workarounds suggested below that will work.Jeffereyjefferies
I have yet to find an answer as to why this is being appended. The included Facebook posts in this thread aren't helpful.Ilk
Anyone know why I am getting this and NOTHING else.Bogoch
2016 March, still happening :PHyperemia
April 2016 still happening. -_-Bondswoman
May 2016 still happening :-)Maltose
FYI, it will not be fixedDeficient
2017, what the zuckPhlebitis
May 2017, still....Ambrosane
June 2017 :) still happening. #_=_Oliva
July 2017... yada yada yadaAfore
August 2017 same shitBearberry
November 2017, nothing's change ..Burnout
December 2017, STILL HAPPENINGFinnougric
Welcome to 2018! Yep.. still happening.Bertolde
February 2018, bug still happening...Inhabited
March 2018..yep still happeningSimulation
31st May 2018.., #_=_ FTW!Alpenglow
July 27th, 2018. It is still happening!Tardigrade
1 August 2018 still happening! :(Unqualified
29 September 2018... GrrrReflectance
October 2018 still happening :(Farouche
Jan 2019 still happeningValentijn
April 2019 still happening :)Gilmour
August 2019, no news on this issue x)Mayfly
April 2020 and I still see itDagall
Christmas 2020 still happening :(Kief
February 2021 is still a problem for meFermium
April 2021, v10.0 still happeningPrairie
To me this actual seems like a browser bug, because the #_=_ comes from the first redirect, but the second redirect doesn't have a hash, so it seems like a vulnerability that the hash given in a set of redirect is kept even on further redirects (so long as the last one doesn't have a hash)..Contravallation
February 2022, still happening! <(^.^<)Sphene
Found you in September 2022.. because why dis happening?Disbelieve
2023, still happening!Political
E
246

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>
Erogenous answered 4/9, 2011 at 7:14 Comment(19)
I'm having issues with this too, using Java though, but the hash thing is in the way nonetheless.Cither
what field is left blank? This is very crypticSuit
Agreed. If you're having this problem, please comment on the Facebook Platform Update: developers.facebook.com/blog/post/552Erogenous
@Erogenous Update almost works for me, I still get a hash (/#) on the end. Not happy with FB.Tippett
I still get the /# as well. anyone update here? to get the # removedMeso
This solution will erase the hash: <script type="text/javascript"> var idx=window.location.toString().indexOf("#_=_"); if (idx>0) { window.location = window.location.toString().substring(0, idx); } </script> Just be sure this to be the first tag in the head element.Pilgrimage
what does this refer to: "when this field is left blank" ... what 'field'?Segalman
typical facebook documentation.Baerl
This link is broken. developers.facebook.com/bugs/196125357123225 Does anyone have the updated bug link? I'd like to see the status of this issue in the facebook apiSerrato
I noticed your bug report here: developers.facebook.com/bugs/1424488987806270 I also tried my hand at searching "fragment request_uri" with the same results.Erogenous
Hey, @niftylettuce here, can you please update your answer to this? github.com/jaredhanson/passport-facebook/issues/…Senate
Why is this being appended? The Facebook documentation doesn't say.Ilk
I think this solution DOES work. You just need to use the https protocol in your callback URL.Polygnotus
As @GorgiRankovski cited above, this works perfectly, just ensure it's the FIRST script that runs in the HEAD element. Same is true for PapaSierra's solution below.Aril
Only to complete this discussion: Facebook also explains, why they added this anchor - go to: https://developers.facebook.com/bugs/318390728250352/Row
that will leave the # sign :(Bearberry
@Erogenous I tried this solution which seems to be point at right direction but the redirect does not work for me. It also redirect to /#_=_. What could I be missing? I am asking in terms of the original answer (not the update). Help would be appreciated.Fulcher
@GorgiRankovski, your solution worked best as it removes the final hash.Antoine
@Erogenous if there's a search string in the initial url, it will be gone. I would just add append window.location.search here: window.history.pushState("", document.title, window.location.pathname + window.location.search);Singleminded
R
125

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.

Rodenticide answered 19/8, 2013 at 0:47 Comment(4)
Worked perfectly for me too. The other solution gets rid of any query parameters.Sphygmo
It does the same thing for google omniauth, so I get an error no route matches, it appends # (hashtag) after request uri https://.....herokua‌​pp.com/auth/google_oa‌​uth2/callback?state=1‌​9feaacfe23423jh5jhhGS‌​DFwb419049ebb18dabdf8‌​&code=4/glrY3-mSlTzwe‌​rwERTEG334eXcn3hOSxGu‌​c51BAlglPa4AU#Excited
Worked for me better than the solution of @Ryan, as it does not delete the query.Row
This solution worked better than the Ryan's solution. I pass some parameters to the url after it goes thru facebook's authentication and Ryan's solution, for some reason just removes every parameter from the url. This solution works perfectly in my case.Edholm
B
68

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/

Bergamot answered 29/1, 2017 at 3:25 Comment(4)
This is the only answer that actually explains why this happens, thanks, I think I'll leave the offending chars in my urls now that I know they're not an issue.Ingoing
This is also implemented by Tumblr in their redirects. (as of mid-'19) Thanks for pointing to the FB explanation. Easily solved in a simplistic Passport app by merely pointing the successful redirect to "/#" instead of just "/" (which explains why I see more trailing octothorps on the web, I think...)Selves
Well for people using vue router they're a big issue.. As it will crash because vue router is expecting a valid js selector which this isn't, they could have just set # with nothing else and the vulnerability would still be mitigated, but no they had to put some more weird stuff requiring dirty workaroundsContravallation
This is so fat bs design it annoys me back and forth. If they want to use redirect_url parameter then allow us to pass query params for that purpose, otherwise the default behaviour will clear themDominicadominical
P
61

if you want to remove the remaining "#" from the url

$(window).on('load', function(e){
  if (window.location.hash == '#_=_') {
    window.location.hash = ''; // for older browsers, leaves a # behind
    history.pushState('', document.title, window.location.pathname); // nice and clean
    e.preventDefault(); // no page reload
  }
})
Pedestrianism answered 9/4, 2012 at 2:2 Comment(3)
$(window).on('load', function(e){ /*likebeats's code*/ } works.Outstrip
i use this code by change e.preventDefault(); to event.preventDefault();Circumnavigate
This code is assuming jQuery, and an onWindowReady event listener taking the argument e.Thine
L
13

Not sure why they're doing this but, you could get around this by reseting the hash at the top of your page:

if (window.location.hash == "#_=_")
  window.location.hash = "";
Lewse answered 21/8, 2011 at 18:54 Comment(0)
T
9

You can also specify your own hash on the redirect_uri parameter for the Facebook callback, which might be helpful in certain circumstances e.g. /api/account/callback#home. When you are redirected back, it'll at least be a hash that corresponds to a known route if you are using backbone.js or similar (not sure about jquery mobile).

Tufthunter answered 30/4, 2013 at 16:43 Comment(0)
L
8

Major annoying, especially for apps that parse the URI and not just read the $_GET... Here's the hack I threw together... Enjoy!

<html xmlns:fb='http://www.facebook.com/2008/fbml'>
<head>
        <script type="text/javascript">
        // Get rid of the Facebook residue hash in the URI
        // Must be done in JS cuz hash only exists client-side
        // IE and Chrome version of the hack
        if (String(window.location.hash).substring(0,1) == "#") {
                window.location.hash = "";
                window.location.href=window.location.href.slice(0, -1);
                }
        // Firefox version of the hack
        if (String(location.hash).substring(0,1) == "#") {
                location.hash = "";
                location.href=location.href.substring(0,location.href.length-3);
                }
        </script>
</head>
<body>
URI should be clean
</body>
</html>
Longitudinal answered 1/1, 2012 at 0:36 Comment(1)
Be careful about making assumptions when parsing any data that you don't create. URI fragment identifiers were spec'd as early as RFC 1738 (in 1994) so if you use a correct URI parser, this should never be a problem.Emissive
N
6

This can become kind of a serious issue if you're using a JS framework with hashbang (/#!/) URLs, e.g. Angular. Indeed, Angular will consider URLs with a non-hashbang fragment as invalid and throw an error :

Error: Invalid url "http://example.com/#_=_", missing hash prefix "#!".

If you're in such a case (and redirecting to your domain root), instead of doing :

window.location.hash = ''; // goes to /#, which is no better

Simply do :

window.location.hash = '!'; // goes to /#!, which allows Angular to take care of the rest
Nagoya answered 3/10, 2013 at 10:7 Comment(3)
1.2+ , this works awesome. For 1.0 and below use window.location.hash = '';Floppy
Yes, I only tested this on 1.2, thanks for the specification !Nagoya
And then there is html5 modeDovekie
O
5

I do not see how this problem is related to facebook AJAX. In fact the issue also occurs with JavaScript disabled and purely redirect based logins.

An example exchange with facebook:

1. GET <https://www.facebook.com/dialog/oauth?client_id=MY_APP_ID&scope=email&redirect_uri=MY_REDIRECT_URL> RESPONSE 302 Found Location: <https://www.facebook.com/connect/uiserver.php?[...]>  
2. GET <https://www.facebook.com/connect/uiserver.php?[...]> RESPONSE 302 Found MY_REDIRECT_URL?code=FB_CODE#_  
3. GET MY_REDIRECT_URL?code=FB_CODE#_  

Happens only with Firefox for me too.

Otorhinolaryngology answered 26/8, 2011 at 13:30 Comment(0)
M
4

Adding this to my redirect page fixed the problem for me ...

if (window.location.href.indexOf('#_=_') > 0) {
    window.location = window.location.href.replace(/#.*/, '');
}
Morgenthaler answered 16/5, 2012 at 6:59 Comment(1)
this causes a window location change, initiating a page refreshKokura
E
3

With angular and angular ui router, you can fix this

    app.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {

      // Make a trailing slash optional for all routes
      // - Note: You'll need to specify all urls with a trailing slash if you use this method.
      $urlRouterProvider.rule(function ($injector, $location) {
        /***
        Angular misbehaves when the URL contains a "#_=_" hash.

        From Facebook:
          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.

        Fix:
          https://mcmap.net/q/73804/-facebook-callback-appends-39-_-_-39-to-return-url#answer-7297873
        ***/
        if ($location.hash() === '_=_'){
          $location.hash(null);
        }

        var path = $location.url();

        // check to see if the path already has a slash where it should be
        if (path[path.length - 1] === '/' || path.indexOf('/?') > -1) {
          return;
        }
        else if (path.indexOf('?') > -1) {
          $location.replace().path(path.replace('?', '/?'));
        }
        else {
          $location.replace().path(path + '/');
        }
      });

      // etc ...
    });
});
Electrodeposit answered 14/11, 2014 at 17:35 Comment(1)
doesn't work here - the route changes before the rule() is appliedTempura
V
3

If you're using vue-router, you can append to the list of routes:

{
  path: '/_=_',
  redirect: '/', // <-- or other default route
},
Virtu answered 12/11, 2018 at 14:37 Comment(0)
P
2

A change was introduced recently in how Facebook handles session redirects. See "Change in Session Redirect Behavior" in this week's Operation Developer Love blog post for the announcement.

Pandora answered 4/9, 2011 at 5:32 Comment(1)
I am not sure, what is he referring to hereSuit
B
2

For me, i make JavaScript redirection to another page to get rid of #_=_. The ideas below should work. :)

function redirect($url){
    echo "<script>window.location.href='{$url}?{$_SERVER["QUERY_STRING"]}'</script>";        
}
Bathroom answered 13/12, 2015 at 14:11 Comment(1)
this is not a good idea I think because you are creating multiple useless requestsSuperintendency
M
1

A workaround that worked for me (using Backbone.js), was to add "#/" to the end of the redirect URL passed to Facebook. Facebook will keep the provided fragment, and not append its own "_=_".

Upon return, Backbone will remove the "#/" part. For AngularJS, appending "#!" to the return URL should work.

Note that the fragment identifier of the original URL is preserved on redirection (via HTTP status codes 300, 301, 302 and 303) by most browsers, unless the redirect URL also has a fragment identifier. This seems to be recommended behaviour.

If you use a handler script that redirects the user elsewhere, you can append "#" to the redirect URL here to replace the fragment identifier with an empty string.

Molybdenum answered 21/10, 2015 at 10:35 Comment(0)
R
1

I know this reply is late, but if you are using passportjs, you might want to see this.

return (req, res, next) => {
    console.log(req.originalUrl);
    next();
};

I have written this middleware and applied it to express server instance, and the original URL I've got is without the "#_=_". Looks like it when we apply passporJS' instance as middleware to the server instance, it doesn't take those characters, but are only visible on the address bar of our browsers.

Rectitude answered 1/9, 2017 at 21:18 Comment(1)
"#_=_" it only available on the client. Review: en.wikipedia.org/wiki/Fragment_identifierRidings
P
1

I use this one, to delete '#' symbol as well.

<script type="text/javascript">
    if (window.location.hash && window.location.hash == '#_=_') {
        window.location.href = window.location.href.split('#_=_')[0];
    }
</script>
Pacifier answered 20/11, 2017 at 17:21 Comment(0)
L
1

For PHP SDK users

I fixed the problem simply by removing the extra part before forwarding.

 $loginURL = $helper->getLoginUrl($redirectURL, $fbPermissions);
 $loginURL = str_replace("#_=_", "", $loginURL);
 header("Location: " . $loginURL);
Lynching answered 6/7, 2018 at 20:32 Comment(0)
F
1

This would remove the appended characters to your url

<script type="text/javascript">
 var idx=window.location.toString().indexOf("#_=_"); 
   if (idx > 0) { 
     window.location = window.location.toString().substring(0, idx); 
   } 
</script>
Farouche answered 4/10, 2018 at 6:19 Comment(0)
R
0

Using Angular 2 (RC5) and hash-based routes, I do this:

const appRoutes: Routes = [
  ...
  {path: '_', redirectTo: '/facebookLoginSuccess'},
  ...
]

and

export const routing = RouterModule.forRoot(appRoutes, { useHash: true });

As far as I understand, the = character in the route is interpreted as part of optional route parameters definition (see https://angular.io/docs/ts/latest/guide/router.html#!#optional-route-parameters), so not involved in the route matching.

Retroflex answered 8/9, 2016 at 14:10 Comment(0)
A
0

The easiest and clean solution to remove "#_=_" (PHP):

Instead of "header("Location: xxx.php");" to use "echo ("location.href = 'xxx.php';");"

Atory answered 26/1, 2020 at 23:6 Comment(0)
E
0

For those who are looking for simple answer Just add this , it worked for me

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

Check the full answer by Paul Schwarz as well

https://mcmap.net/q/73804/-facebook-callback-appends-39-_-_-39-to-return-url

Expert answered 6/8, 2020 at 15:28 Comment(2)
This is just a copy/paste of my answer https://mcmap.net/q/73804/-facebook-callback-appends-39-_-_-39-to-return-urlRodenticide
I just add this to reference my self and updated the answer with link to you full answer all credits goes to you @PaulSchwarz I am also referring to your answer and already up voted yoursExpert
K
0

If you use Vue router with createWebHistory history mode (so not hash mode), then you can add the following after you create the router instance (can be done before you use it with the app:

router.isReady().then(() => {
  if (router.currentRoute.value.hash === "#_=_") {
    router.replace({ ...router.currentRoute.value, hash: '' })
  }
})
Kinsley answered 30/11, 2023 at 7:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.