Facebook callback has started appending #_=_
hash underscore to the Return URL
Does anyone know why? What is the solution?
Facebook callback has started appending #_=_
hash underscore to the Return URL
Does anyone know why? What is the solution?
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>
https
protocol in your callback URL. –
Polygnotus window.history.pushState("", document.title, window.location.pathname + window.location.search);
–
Singleminded 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:
fragment
is #_=_
.window.replaceState
method.
#
and taking only the first part.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. ;-)#_-_
. 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/
#
with nothing else and the vulnerability would still be mitigated, but no they had to put some more weird stuff requiring dirty workarounds –
Contravallation 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
}
})
e
. –
Thine 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 = "";
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).
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>
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
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.
Adding this to my redirect page fixed the problem for me ...
if (window.location.href.indexOf('#_=_') > 0) {
window.location = window.location.href.replace(/#.*/, '');
}
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 ...
});
});
If you're using vue-router, you can append to the list of routes:
{
path: '/_=_',
redirect: '/', // <-- or other default route
},
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.
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>";
}
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.
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.
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>
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);
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>
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.
The easiest and clean solution to remove "#_=_" (PHP):
Instead of "header("Location: xxx.php");" to use "echo ("location.href = 'xxx.php';");"
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
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: '' })
}
})
© 2022 - 2024 — McMap. All rights reserved.
#_=_
is in place, then even though you do aResponse.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