O retorno de chamada do Facebook anexa '# _ = _' ao URL de retorno

479

O retorno de chamada do Facebook começou a acrescentar #_=_sublinhado de hash ao URL de retorno

Alguem sabe por quê? Qual é a solução?

zing ming
fonte
35
Alguma idéia de como o Facebook acrescenta esses caracteres? O Facebook redireciona para meu manipulador, onde lida com o redirecionamento para o URL de retorno, mas os caracteres ainda são anexados ao URL.
Ben Foster
4
@BenFoster Acho que você descobrirá se usar o Fiddler ou algo semelhante que, quando o FB redireciona para o manipulador, ele já #_=_está no lugar, mesmo que você faça o Response.Redirectque realmente deseja, o navegador mantém o hash , e é por isso que são apenas as soluções alternativas do lado do cliente sugeridas abaixo que funcionarão.
AakashM
17
2017, what the zuck
Ejonas GGgg 2/17/17
6
Maio 2017, ainda ....
Mirko
5
Março ainda 2018..yep acontecendo
John Rogerson

Respostas:

239

através das atualizações da plataforma do Facebook :

Alteração no comportamento de redirecionamento de sessão

Nesta semana, começamos a adicionar um fragmento # ____ = ____ ao redirect_uri quando este campo é deixado em branco. Verifique se seu aplicativo pode lidar com esse comportamento.

Para evitar isso, defina o redirect_uri na sua solicitação de URL de login da seguinte forma: (usando o Facebook php-sdk)

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

ATUALIZAR

O acima é exatamente como a documentação diz para corrigir isso. No entanto, a solução documentada do Facebook não funciona. Por favor, considere deixar um comentário no blog do Facebook Platform Updates e siga este bug para obter uma resposta melhor. Até lá, adicione o seguinte à sua tag head para resolver esse problema:

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

Ou uma alternativa mais detalhada (obrigado 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>
Ryan
fonte
10
qual campo é deixado em branco? Isso é muito enigmático
user210504
11
@ Ryan Update quase funciona para mim, ainda recebo um hash (/ #) no final. Não está feliz com o FB.
precisa saber é o seguinte
2
Eu ainda recebo o / # também. alguém atualiza aqui? para remover o # #
Tian Loon
6
Essa solução apagará o hash: <script type = "text / javascript"> var idx = window.location.toString (). IndexOf ("# _ = _"); if (idx> 0) {window.location = window.location.toString (). substring (0, idx); } </script> Apenas certifique-se de que essa seja a primeira tag no elemento head.
Gorgi Rankovski
1
Notei seu relatório de erros aqui: developers.facebook.com/bugs/1424488987806270 Também tentei pesquisar "fragment request_uri" com os mesmos resultados.
Ryan
115

TL; DR

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

Versão completa com instruções passo a passo

// 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 = "";

    }

}

Passo a passo:

  1. Nós só vai entrar no bloco de código se o fragmenté #_=_.
  2. Verifique se o navegador suporta o window.replaceStatemétodo HTML5 .
    1. Limpe o URL dividindo #e utilizando apenas a primeira parte.
    2. Diga historypara substituir o estado atual da página pelo URL limpo. Isso modifica a entrada do histórico atual em vez de criar uma nova. O que isso significa é que os botões voltar e avançar funcionam da maneira que você deseja. ;-)
  3. Se o navegador não suportar os incríveis métodos de histórico do HTML 5, limpe o URL da melhor maneira possível, definindo o hash como string vazia. Este é um fallback ruim, porque ainda deixa um hash à direita (exemplo.com/#) e também adiciona uma entrada no histórico; portanto, o botão Voltar leva você de volta a #_-_.

Saiba mais sobre history.replaceState.

Saiba mais sobre window.location.

Paul Schwarz
fonte
2
Funcionou perfeitamente para mim também. A outra solução se livra de quaisquer parâmetros de consulta.
AdeelMufti
Ele faz o mesmo com o Google Omniauth, por isso, recebo um erro que não corresponde à rota, acrescenta # (hashtag) após a solicitação uri https: //.....herokua‌ pp.com/auth/google_oa‌ uth2 / callback? state = 1‌ 9feaacfe23423jh5jhhGS‌ DFwb419049ebb18dabdf8‌ & code = 4 / glrY3-mSlTzwe‌ rwERTEG334eXcn3hOSxGu‌ c51BAlglPa4AU #
17 de
Funcionou para mim melhor do que a solução do @Ryan, pois não exclui a consulta.
Olivmir
Essa solução funcionou melhor que a solução do Ryan. Passo alguns parâmetros para o URL depois que ele passa pela autenticação do Facebook e pela solução de Ryan, por algum motivo, apenas remove todos os parâmetros do URL. Esta solução funciona perfeitamente no meu caso.
BlueSun3k1
59

se você deseja remover o "#" restante do 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
  }
})
likebeats
fonte
6
$ (window) .on ('load', function (e) {/ * código do likebeats * /} funciona.
ISHITOYA Kentaro
1
eu uso esse código alterando e.preventDefault (); para event.preventDefault ();
usar o seguinte
Esse código está assumindo jQuery e um ouvinte de evento onWindowReady aceitando o argumento e.
Jason Sperske
49

Isso foi implementado pelo Facebook por design por razões de segurança. Aqui está a explicação de Eric Osgood, um membro da equipe do Facebook:

Isso foi marcado como 'por design' porque evita uma potencial vulnerabilidade de segurança.

Alguns navegadores anexarão o fragmento de hash de um URL ao final de um novo URL para o qual eles foram redirecionados (se esse novo URL não possuir um fragmento de hash).

Por exemplo, se example1.com retornar um redirecionamento para example2.com, um navegador que vá para example1.com # abc irá para example2.com # abc, e o conteúdo do fragmento de hash de example1.com estará acessível para um script no exemplo2 .com.

Como é possível ter um fluxo de autenticação redirecionado para outro, seria possível ter dados de autenticação confidenciais de um aplicativo acessíveis a outro.

Isso é atenuado anexando um novo fragmento de hash ao URL de redirecionamento para impedir o comportamento desse navegador.

Se a estética ou o comportamento do cliente da URL resultante forem preocupantes, seria possível usar window.location.hash (ou mesmo um redirecionamento próprio do servidor) para remover os caracteres incorretos.

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

Mark Murphy
fonte
9
Esta é a única resposta que realmente explica por que isso acontece. Obrigado, acho que deixarei os caracteres ofensivos em meus URLs agora que sei que eles não são um problema.
stephenmurdoch
1
Isso também é implementado pelo Tumblr em seus redirecionamentos. (em meados de 1919) Obrigado por apontar para a explicação do FB. Facilmente resolvido em um passaporte aplicativo simplista por apenas apontando o redirecionamento de sucesso com "/ #" em vez de apenas "/" (o que explica por que vejo octothorps mais à direita na web, eu acho ...)
RL Brown
10

Não sei por que eles estão fazendo isso, mas você pode contornar isso redefinindo o hash na parte superior da sua página:

if (window.location.hash == "#_=_")
  window.location.hash = "";
mixmasteralan
fonte
9

Você também pode especificar seu próprio hash no redirect_uriparâmetro para retorno de chamada do Facebook, o que pode ser útil em determinadas circunstâncias, por exemplo /api/account/callback#home. Quando você é redirecionado de volta, pelo menos será um hash que corresponde a uma rota conhecida se você estiver usando o backbone.js ou semelhante (não tem certeza sobre o jquery mobile).

pkiddie
fonte
8

O Facebook usa um quadro e dentro dele tudo funciona usando a comunicação AJAX. O maior problema nesse caso é preservar o estado da página atual. Até onde eu entendi, o Facebook decidiu usar âncoras simuladas. Isso significa que, se você clicou em algum lugar, eles simulam isso como uma âncora dentro da sua página e, quando a comunicação AJAX é iniciada, eles também alteram o bit de âncora da sua URL.

Essa solução ajuda normalmente quando você tenta recarregar a página (não ENTER, pressione F5), porque seu navegador envia todo o URL com âncoras para o servidor do Facebook. Portanto, o Facebook capta o estado mais recente (o que você vê) e você pode continuar a partir daí.

Quando o retorno de chamada retorna #_=_, significa que a página estava em seu estado básico antes de sair. Como essa âncora é analisada pelo navegador, você não precisa se preocupar com isso.

Sándor Tóth
fonte
2
Se você tem uma estrutura javascript como backbone ou brasa, é um problema, pois tudo depois que o hash é interpretado pelo roteador
Rudi
1
Os identificadores de fragmento de URL ("âncoras") não são enviados ao navegador em uma solicitação. Além disso, esta pergunta é sobre o OAuth, não sobre o site principal da área de trabalho. O motivo disso é a segurança do OAuth - impedindo ataques devido à criação de um URI de redirecionamento malicioso.
AndrewF
8

Muito chato, especialmente para aplicativos que analisam o URI e não apenas leem o $ _GET ... Aqui está o truque que joguei juntos ... Divirta-se!

<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>
Jeremy Whitt
fonte
Tenha cuidado ao fazer suposições ao analisar quaisquer dados que você não criar. Os identificadores de fragmentos de URI foram especificados tão cedo quanto a RFC 1738 (em 1994); portanto, se você usar um analisador de URI correto, isso nunca deve ser um problema.
AndrewF
6

Isso pode se tornar um problema sério se você estiver usando uma estrutura JS com URLs hashbang (/ #! /), Por exemplo, Angular. De fato, o Angular considerará os URLs com um fragmento não hashbang inválido e gerará um erro:

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

Se você estiver nesse caso (e redirecionando para a raiz do seu domínio), em vez de fazer:

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

Simplesmente faça:

window.location.hash = '!'; // goes to /#!, which allows Angular to take care of the rest
neemzy
fonte
1.2+, isso funciona demais. Para 1.0 e abaixo, use window.location.hash = '';
Pradeep Mahdevu
1
Sim, só testei isso no 1.2, obrigado pela especificação!
neemzy
E depois há o modo html5
rocketspacer
5

Não vejo como esse problema está relacionado ao facebook AJAX. De fato, o problema também ocorre com o JavaScript desabilitado e com logins puramente redirecionados.

Um exemplo de troca com o 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#_  

Também acontece com o Firefox para mim.

Sebastian Tusk
fonte
4

Adicionar isso à minha página de redirecionamento corrigiu o problema para mim ...

if (window.location.href.indexOf('#_=_') > 0) {
    window.location = window.location.href.replace(/#.*/, '');
}
neokio
fonte
1
isso faz com que uma mudança local da janela, iniciando uma atualização de página
rpearce
3

Com o roteador angular e angular da interface do usuário, você pode corrigir isso

    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:
          http://stackoverflow.com/questions/7131909/facebook-callback-appends-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 ...
    });
});
rebelde
fonte
não funciona aqui - a rota é alterada antes que a regra () seja aplicada
Maël Nison
3

Se você estiver usando o vue-router, poderá anexar à lista de rotas:

{
  path: '/_=_',
  redirect: '/', // <-- or other default route
},
Slawomir
fonte
2

Uma mudança foi introduzida recentemente na maneira como o Facebook lida com os redirecionamentos de sessão. Consulte "Mudança no comportamento de redirecionamento de sessão" na postagem do blog Operation Developer Love desta semana para o anúncio.

Dhiren Patel
fonte
1
Eu não tenho certeza, o que ele está se referindo aqui
#
2

Para mim, faço o redirecionamento de JavaScript para outra página para me livrar #_=_. As idéias abaixo devem funcionar. :)

function redirect($url){
    echo "<script>window.location.href='{$url}?{$_SERVER["QUERY_STRING"]}'</script>";        
}
Eng Cy
fonte
esta não é uma boa ideia eu acho que porque você está criando várias solicitações inúteis
Jacek Pietal
1

Uma solução alternativa que funcionou para mim (usando o Backbone.js) foi adicionar "# /" ao final do URL de redirecionamento passado para o Facebook. O Facebook manterá o fragmento fornecido e não anexará seu próprio "_ = _".

Ao retornar, o Backbone removerá a parte "# /". Para AngularJS, anexando "#!" para o URL de retorno deve funcionar.

Observe que o identificador de fragmento da URL original é preservado no redirecionamento (por meio dos códigos de status HTTP 300, 301, 302 e 303) pela maioria dos navegadores, a menos que a URL de redirecionamento também tenha um identificador de fragmento. Este parece ser um comportamento recomendado .

Se você usar um script manipulador que redirecione o usuário para outro lugar, poderá anexar "#" ao URL de redirecionamento aqui para substituir o identificador de fragmento por uma string vazia.

Ivo Smits
fonte
1

Sei que esta resposta está atrasada, mas se você estiver usando passportjs, poderá ver isso.

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

Eu escrevi esse middleware e o apliquei para expressar a instância do servidor, e o URL original que obtive é sem o "#_=_". Parece que quando aplicamos a instância do passporJS como middleware à instância do servidor, ele não aceita esses caracteres, mas é visível apenas na barra de endereços de nossos navegadores.

Krishna
fonte
3
"# _ = _" está disponível apenas no cliente. Resenha: en.wikipedia.org/wiki/Fragment_identifier
alditis
1

Eu uso este, para excluir o símbolo '#' também.

<script type="text/javascript">
    if (window.location.hash && window.location.hash == '#_=_') {
        window.location.href = window.location.href.split('#_=_')[0];
    }
</script>
Simon
fonte
0

Usando Angular 2 (RC5) e rotas baseadas em hash, faço isso:

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

e

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

Tanto quanto eu entendo, o =caractere na rota é interpretado como parte da definição opcional de parâmetros de rota (consulte https://angular.io/docs/ts/latest/guide/router.html#!#optional-route-parameters ) , portanto, não envolvido na correspondência de rota.

rcomblen
fonte
0

Para usuários do PHP SDK

Corrigi o problema simplesmente removendo a parte extra antes de encaminhar.

 $loginURL = $helper->getLoginUrl($redirectURL, $fbPermissions);
 $loginURL = str_replace("#_=_", "", $loginURL);
 header("Location: " . $loginURL);
Nanoripper
fonte
0

Isso removeria os caracteres anexados ao seu URL

<script type="text/javascript">
 var idx=window.location.toString().indexOf("#_=_"); 
   if (idx > 0) { 
     window.location = window.location.toString().substring(0, idx); 
   } 
</script>
Rotimi
fonte
0

A solução mais fácil e limpa para remover "# _ = _" (PHP):

Em vez de "header (" Location: xxx.php ");" para usar "echo (" location.href = 'xxx.php'; ");"

user3806621
fonte