La devolución de llamada de Facebook agrega '# _ = _' a la URL de retorno

479

La devolución de llamada de Facebook ha comenzado a agregar #_=_subrayado hash a la URL de retorno

¿Alguien sabe por qué? ¿Cuál es la solución?

zing ming
fuente
35
¿Alguna idea de cómo Facebook agrega estos personajes? Facebook redirige a mi controlador donde luego manejo la redirección a la url de retorno, sin embargo, los caracteres todavía se agregan a la url.
Ben Foster,
44
@BenFoster Creo que encontrará si usa Fiddler o similar que cuando FB se reduce a su controlador, #_=_está en su lugar, entonces, aunque hace un Response.Redirecta donde realmente quiere hacerlo, el navegador mantiene el hash , por eso solo funcionarán las soluciones alternativas del lado del cliente sugeridas a continuación.
AakashM
17
2017, qué demonios
Ejonas GGgg
66
Mayo de 2017, todavía ...
Mirko
55
Marzo de 2018 ... sigue ocurriendo
John Rogerson

Respuestas:

239

a través de las actualizaciones de la plataforma de Facebook :

Cambio en el comportamiento de redireccionamiento de sesión

Esta semana, comenzamos a agregar un fragmento # ____ = ____ al redirect_uri cuando este campo se deja en blanco. Asegúrese de que su aplicación pueda manejar este comportamiento.

Para evitar esto, configure el redirect_uri en su solicitud de inicio de sesión de la siguiente manera: (usando Facebook php-sdk)

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

ACTUALIZAR

Lo anterior es exactamente como dice la documentación para solucionar esto. Sin embargo, la solución documentada de Facebook no funciona. Considere dejar un comentario en la publicación del blog Actualizaciones de la plataforma de Facebook y siga este error para obtener una mejor respuesta. Hasta entonces, agregue lo siguiente a su etiqueta principal para resolver este problema:

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

O una alternativa más detallada (gracias 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
fuente
10
¿Qué campo se deja en blanco? Esto es muy críptico
user210504
11
@Ryan Update casi funciona para mí, todavía obtengo un hash (/ #) al final. No estoy contento con FB.
LenPopLilly
2
Todavía obtengo el / # también. alguien actualiza aquí? para obtener el # eliminado
Tian Loon
66
Esta solución borrará el hash: <script type = "text / javascript"> var idx = window.location.toString (). IndexOf ("# _ = _"); if (idx> 0) {window.location = window.location.toString (). substring (0, idx); } </script> Solo asegúrese de que esta sea la primera etiqueta en el elemento head.
Gorgi Rankovski
1
Noté su informe de error aquí: developers.facebook.com/bugs/1424488987806270 También probé a buscar "fragment request_uri" con los mismos resultados.
Ryan
115

TL; DR

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

Versión completa con instrucciones paso a paso.

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

    }

}

Paso a paso:

  1. Solo entraremos en el bloque de código si fragmentes así #_=_.
  2. Compruebe si el navegador admite el window.replaceStatemétodo HTML5 .
    1. Limpie la URL dividiéndose #y tomando solo la primera parte.
    2. Indique historyque reemplace el estado actual de la página con la URL limpia. Esto modifica la entrada del historial actual en lugar de crear una nueva. Lo que esto significa es que los botones de retroceso y avance funcionarán de la manera que desee. ;-)
  3. Si el navegador no admite los increíbles métodos de historial de HTML 5, simplemente limpie la URL lo mejor que pueda configurando el hash para vaciar la cadena. Este es un retroceso pobre porque todavía deja un hash final (example.com/#) y también agrega una entrada en el historial, por lo que el botón Atrás lo llevará de regreso #_-_.

Más información sobre history.replaceState.

Más información sobre window.location.

Paul Schwarz
fuente
2
Funcionó perfectamente para mí también. La otra solución elimina los parámetros de consulta.
AdeelMufti
Hace lo mismo para google omniauth, por lo que recibo un error que no coincide con la ruta, agrega # (hashtag) después de la solicitud uri https: //.....herokua‌ pp.com/auth/google_oa‌ uth2 / callback? state = 1‌ 9feaacfe23423jh5jhhGS‌ DFwb419049ebb18dabdf8‌ & code = 4 / glrY3-mSlTzwe‌ rwERTEG334eXcn3hOSxGu‌ c51BAlglPa4AU #
Shalafister's
Me funcionó mejor que la solución de @Ryan, ya que no elimina la consulta.
olivmir
Esta solución funcionó mejor que la solución de Ryan. Paso algunos parámetros a la url después de pasar por la autenticación de Facebook y la solución de Ryan, por alguna razón solo elimina todos los parámetros de la url. Esta solución funciona perfectamente en mi caso.
BlueSun3k1
59

si desea eliminar el "#" restante de la 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
  }
})
latidos similares
fuente
66
$ (ventana) .on ('cargar', funciona la función (e) {/ * código likebeats * /}.
ISHITOYA Kentaro
1
uso este código por cambio e.preventDefault (); a event.preventDefault ();
printf
Este código supone jQuery, y un detector de eventos onWindowReady toma el argumento e.
Jason Sperske
49

Esto fue implementado por Facebook por diseño por razones de seguridad. Aquí está la explicación de Eric Osgood, miembro del equipo de Facebook:

Esto se ha marcado como 'por diseño' porque evita una posible vulnerabilidad de seguridad.

Algunos navegadores agregarán el fragmento de hash de una URL al final de una nueva URL a la que han sido redirigidos (si esa nueva URL no tiene un fragmento de hash).

Por ejemplo, si example1.com devuelve una redirección a example2.com, entonces un navegador que vaya a example1.com # abc irá a example2.com # abc, y el contenido del fragmento hash de example1.com sería accesible para un script en example2 .com.

Dado que es posible tener un flujo de autenticación redirigido a otro, sería posible tener acceso a datos confidenciales de autenticación de una aplicación a otra.

Esto se mitiga agregando un nuevo fragmento de hash a la URL de redireccionamiento para evitar este comportamiento del navegador.

Si la estética, o el comportamiento del lado del cliente, de la URL resultante son motivo de preocupación, sería posible usar window.location.hash (o incluso una redirección propia del lado del servidor) para eliminar los caracteres ofensivos.

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

Mark Murphy
fuente
99
Esta es la única respuesta que realmente explica por qué sucede esto, gracias, creo que dejaré los caracteres ofensivos en mis URL ahora que sé que no son un problema.
stephenmurdoch
1
Tumblr también implementa esto en sus redireccionamientos. (a mediados de '19) Gracias por señalar la explicación de FB. Se resuelve fácilmente en una aplicación simplista de Passport simplemente señalando la redirección exitosa a "/ #" en lugar de solo "/" (lo que explica por qué veo más octothorps finales en la web, creo ...)
RL Brown
10

No estoy seguro de por qué están haciendo esto, pero puedes evitarlo restableciendo el hash en la parte superior de tu página:

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

También puede especificar su propio hash en el redirect_uriparámetro para la devolución de llamada de Facebook, lo que podría ser útil en ciertas circunstancias, por ejemplo /api/account/callback#home. Cuando se lo redirige hacia atrás, al menos será un hash que corresponde a una ruta conocida si está utilizando backbone.js o similar (no estoy seguro acerca de jquery mobile).

pkiddie
fuente
8

Facebook usa un marco y dentro de él todo funciona mediante la comunicación AJAX. El mayor problema en este caso es preservar el estado actual de la página. Según tengo entendido, Facebook decidió usar anclas simuladas. Esto significa que si hizo clic en algún lugar, simulan eso como un ancla dentro de su página, y cuando comienza la comunicación AJAX, también cambian el bit de anclaje de su URL.

Esta solución le ayuda normalmente cuando intenta recargar la página (no ENTER, presione F5), porque su navegador envía la URL completa con anclajes al servidor de Facebook. Por lo tanto, Facebook recoge el último estado (lo que ves) y luego puedes continuar desde allí.

Cuando la devolución de llamada regresa #_=_significa que la página estaba en su estado básico antes de abandonarla. Debido a que el navegador analiza este anclaje, no debe preocuparse por ello.

Sándor Tóth
fuente
2
Si tiene un framework javascript como backbone o ember, es un problema ya que todo después de que el hash es interpretado por el enrutador
Rudi
1
Los identificadores de fragmentos de URL ("anclas") no se envían al navegador en una solicitud. Además, esta pregunta es sobre OAuth, no sobre el sitio de escritorio principal. La razón de esto es la seguridad de OAuth : evitar ataques debido a la creación de un URI de redireccionamiento malicioso.
AndrewF
8

Muy molesto, especialmente para aplicaciones que analizan el URI y no solo leen $ _GET ... Aquí está el truco que creé ... ¡Disfrútalo!

<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
fuente
Tenga cuidado al hacer suposiciones al analizar cualquier dato que no cree. Los identificadores de fragmentos de URI se especificaron ya en RFC 1738 (en 1994), por lo que si usa un analizador de URI correcto, esto nunca debería ser un problema.
AndrewF
6

Esto puede convertirse en un problema grave si está utilizando un marco JS con URL hashbang (/ #! /), Por ejemplo, Angular. De hecho, Angular considerará las URL con un fragmento no hashbang como no válidas y arrojará un error:

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

Si se encuentra en ese caso (y redirige a la raíz de su dominio), en lugar de hacerlo:

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

Simplemente haz:

window.location.hash = '!'; // goes to /#!, which allows Angular to take care of the rest
neemzy
fuente
1.2+, esto funciona genial. Para 1.0 y versiones inferiores, use window.location.hash = '';
Pradeep Mahdevu
1
Sí, solo probé esto en 1.2, ¡gracias por la especificación!
neemzy
Y luego está el modo html5
rocketspacer
5

No veo cómo se relaciona este problema con Facebook AJAX. De hecho, el problema también ocurre con JavaScript deshabilitado y solo redirecciona inicios de sesión basados.

Un ejemplo de intercambio con 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#_  

Ocurre solo con Firefox para mí también.

Sebastian Tusk
fuente
4

Agregar esto a mi página de redirección solucionó el problema para mí ...

if (window.location.href.indexOf('#_=_') > 0) {
    window.location = window.location.href.replace(/#.*/, '');
}
neokio
fuente
1
esto provoca un cambio de ubicación de la ventana, iniciando una actualización de la página
rpearce
3

Con el enrutador ui angular y angular, puede solucionar esto

    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
fuente
no funciona aquí - la ruta cambia antes de que se aplique la regla ()
Maël Nison
3

Si está utilizando vue-router, puede agregar a la lista de rutas:

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

Recientemente se introdujo un cambio en cómo Facebook maneja las redirecciones de sesión. Consulte "Cambio en el comportamiento de redireccionamiento de sesión" en la publicación del blog Operation Developer Love de esta semana para ver el anuncio.

Dhiren Patel
fuente
1
No estoy seguro, ¿a qué se refiere aquí
User210504
2

Para mí, hago la redirección de JavaScript a otra página para deshacerme de él #_=_. Las siguientes ideas deberían funcionar. :)

function redirect($url){
    echo "<script>window.location.href='{$url}?{$_SERVER["QUERY_STRING"]}'</script>";        
}
Eng Cy
fuente
esta no es una buena idea, creo, porque estás creando múltiples solicitudes inútiles
Jacek Pietal
1

Una solución que funcionó para mí (usando Backbone.js) fue agregar "# /" al final de la URL de redireccionamiento que se pasó a Facebook. Facebook conservará el fragmento proporcionado y no agregará su propio "_ = _".

Al regresar, Backbone eliminará la parte "# /". Para AngularJS, agregar "#!" a la URL de retorno debería funcionar.

Tenga en cuenta que el identificador de fragmentos de la URL original se conserva en la redirección (a través de los códigos de estado HTTP 300, 301, 302 y 303) por la mayoría de los navegadores, a menos que la URL de redireccionamiento también tenga un identificador de fragmento. Este parece ser un comportamiento recomendado .

Si utiliza un script de controlador que redirige al usuario a otra parte, puede agregar "#" a la URL de redireccionamiento aquí para reemplazar el identificador de fragmento con una cadena vacía.

Ivo Smits
fuente
1

Sé que esta respuesta es tardía, pero si está utilizando passportjs, es posible que desee ver esto.

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

He escrito este middleware y lo he aplicado a la instancia del servidor express, y la URL original que tengo no tiene el "#_=_". Parece que cuando aplicamos la instancia de passporJS como middleware a la instancia del servidor, no toma esos caracteres, pero solo son visibles en la barra de direcciones de nuestros navegadores.

Krishna
fuente
3
"# _ = _" solo está disponible en el cliente. Revisión: en.wikipedia.org/wiki/Fragment_identifier
alditis
1

Yo uso este, para eliminar el símbolo '#' también.

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

Usando Angular 2 (RC5) y rutas basadas en hash, hago esto:

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

y

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

Por lo que yo entiendo, el =carácter en la ruta se interpreta como parte de la definición de parámetros de ruta opcionales (ver https://angular.io/docs/ts/latest/guide/router.html#!#optional-route-parameters ) , por lo que no participa en la coincidencia de ruta.

rcomblen
fuente
0

Para usuarios de PHP SDK

Solucioné el problema simplemente quitando la parte adicional antes de reenviar.

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

Esto eliminaría los caracteres agregados a su URL

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

La solución más fácil y limpia para eliminar "# _ = _" (PHP):

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

usuario3806621
fuente