Volver a iniciar y autenticación básica HTTP

Los desarrolladores web conocen desde hace tiempo el problema de iniciar sesión e iniciar sesión en sitios protegidos por la Autorización básica HTTP. Aunque existen otros métodos de autenticación que no sufren este problema, la autorización básica sigue siendo la mejor opción. La red tiene suficientes materiales que describen varias soluciones generales y particulares. Pero todos ellos, que encontré, desafortunadamente, describen solo algunas soluciones parciales que funcionan en un navegador y no funcionan en otro. Con Cat, doy un resultado final generalizado de mi investigación sobre este problema.

Haré una reserva de inmediato que no soy un desarrollador front-end, y no profundicé en toda la variedad de "marcas" y versiones de navegador. Solo mainstream con versiones relevantes al momento de la escritura. Para la mayoría de las tareas web, esto es suficiente. Para aquellos desarrolladores que necesitan soporte para todo el "zoológico" de navegadores, el artículo puede ser un buen punto de partida.

La esencia del problema es que el estándar de Autorización Básica HTTP no permite el registro. Generalmente. Cuando ingresa a una página protegida por la Autorización básica, el navegador muestra su propia ventana con una solicitud de inicio de sesión / contraseña y, con un inicio de sesión exitoso, los guarda en algún lugar de sus profundidades. Luego, todas las solicitudes posteriores a otras páginas protegidas de este sitio utilizarán automáticamente estos valores en el encabezado de Autorización:
Autorización: Basic bm9uZTpub25l
donde bm9uZTpub25l es el enlace de inicio de sesión / contraseña codificado en base64 ninguno: ninguno (su nombre de usuario y contraseña pueden ser diferentes)

Para iniciar sesión, solo necesita restablecer el antiguo nombre de usuario / contraseña en esas profundidades del navegador y en ellos lugar para grabar nuevos. Aquí es donde nos espera una sorpresa. Como ningún estándar regula esta acción, cada navegador lo hace según su propio entendimiento.

Peor aún, dado que la ventana de solicitud de inicio de sesión / contraseña es su propia ventana del navegador, que ya responde solo a los encabezados de página HTTP, esta ventana aparece antes de cualquier procesamiento del cuerpo de la página. Es decir, ejecutar algunos JavaScript cuando recibe una respuesta del servidor con un estado de 401 no funciona. Ante el usuario, esta ventana aparecerá una y otra vez con una solicitud repetida de inicio de sesión / contraseña.

De hecho, este punto es crítico, ya que para casi todos los navegadores, la única forma de cerrar sesión es enviar una solicitud al servidor con un nombre de usuario / contraseña obviamente incorrecto, recibir una respuesta del servidor con el estado 401 y luego restablecer el caché de inicio de sesión / contraseña del navegador. Pero si al mismo tiempo el navegador no le permite procesar la respuesta 401 y continuar el proceso de inicio de sesión para el nuevo usuario, tomar el control incluso en la etapa de leer encabezados HTTP y lanzar la misma forma de autorización en la que ingresa no funcionará en su cara, entonces esto Ya es un problema. No importa si esta es una solicitud normal por referencia o XMLHttpRequest o fetch. El navegador aún toma el control en la etapa de analizar los encabezados.

Entonces…

Datos iniciales:


  1. Hay una página logout.html separada en la que se encuentra toda la lógica en el script javascript, partes de las cuales se dan en el curso de la presentación.
  2. URL especificada: dirección de la página para redirigir después de iniciar sesión correctamente
  3. Se especifica Url401: la dirección de la página que siempre devuelve el error HTTP 401 (no autorizado)

// file logout.html
const url = new URL("http://mysite.com/");
const url401 = new URL("http://mysite.com/401");

Explorador de Internet


Nuestro producto no requiere soporte para este navegador, por lo que la solución no la he probado personalmente. Sin embargo, según Google, hay una solución para ello, y quizás sea la más simple y elegante de todas. Además, nunca he encontrado ninguna información de que esta solución para los navegadores de Microsoft haya perdido relevancia:

if (document.execCommand("ClearAuthenticationCache")) {
    window.location.assign(url);
}

En IE, hay un método ClearAuthenticationCache que descarta "esos intestinos muy profundos". Simple y elegante Y no bailar con la página auxiliar 401. No sé si este método funciona en Edge. Probablemente si.

La construcción document.execCommand devuelve verdadero si el método existe y "funcionó". Luego, window.location.assign (url) redirige al usuario para ingresar un nuevo nombre de usuario y contraseña

Firefox (72.0.1)


En el contexto de nuestra tarea, este es el navegador más problemático. Para él, todavía no existe una solución completa. Durante 15-20 años, una solicitud para el problema indicado ha estado pendiente en el rastreador de errores del equipo de sus desarrolladores. Pero "las cosas siguen ahí". Lo máximo que se puede lograr es la curva razlogin.

Entrada:
Firefox no vacía el caché de contraseñas después de recibir una respuesta 401 a través de XMLHttpRequest o solicitud de búsqueda. Solo a través de una solicitud regular con el nombre de usuario / contraseña en la propia URL. Es decir, algo como
http: // none: none@mysite.com/
El código:

else if (/firefox|iceweasel|fxios/i.test(window.navigator.userAgent)) {
    url.username = 'none';
    url.password = 'none';
    window.location.assign(url);
}

Después de lo cual el usuario recibe un formulario de ingreso de nombre de usuario / contraseña, en el que ingrese, aparecerá una y otra vez. El hecho es que los valores ingresados ​​no anularán los valores de inicio de sesión / contraseña especificados en la URL. Es decir, en lugar de los valores ingresados, un montón de ninguno: ninguno irá al servidor cada vez. Y para iniciar sesión con un nombre diferente, el usuario debe hacer clic en cancelar, ir a la página de inicio (http: //mysite.com/) e ingresar un nuevo nombre de usuario / contraseña allí.

¿Torcido? Pero, por desgracia, no hay otra solución

Google Chrome (79.0.3945.88)


Para Chrome, el método de recuperación funciona muy bien. Pero XMLHttpRequest no funciona (((El caché no se restablece y el registro no ocurre. Además, intenté establecer el inicio de sesión / contraseña como parámetros para el método abierto y establecer el encabezado.

else if (/chrome/i.test(window.navigator.userAgent)) {
    fetch(url401, {
      credentials: 'include',
      headers: {
        'Authorization': 'Basic ' + btoa('none:none'),
        'X-Requested-With': 'XMLHttpRequest'
      }
    }).then(() => {
      window.location.assign(url);
    }).catch(err => console.error(err));
}

Hacemos una solicitud de búsqueda a la página 401 con un nombre de usuario / contraseña obviamente incorrecto, obtenemos una respuesta 401 del servidor, el navegador vacía su caché.

¡IMPORTANTE! El servidor NO debe devolver un encabezado WWW-Authenticate . De lo contrario, el navegador tomará el control y los redireccionamientos desde la página 401 nunca sucederán. Por convención, el servidor no debe devolver este encabezado si X-Requested-With: XMLHttpRequest se especifica en la solicitud . Por lo tanto, el encabezado X-Requested-With se ha agregado a la solicitud .

Safari (12)


Para Safari, la situación es exactamente la opuesta: XMLHttpRequest funciona, pero fetch no funciona

else {
    const xhr = new XMLHttpRequest();
    xhr.open("GET", url401, true, 'none', 'none');
    xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
    xhr.onerror = function(err) {
      console.error(err);
    };
    xhr.onload = function () {
      window.location.assign(url);
    };
    xhr.send()
}

Las acciones son las mismas que en Chrome, solo a través de XMLHttpRequest

Debería funcionar para las versiones de Safari 6+. Las versiones anteriores tenían sus propios errores. En particular, por ejemplo, en las versiones de 5.1, con cada redireccionamiento, el navegador volvió a solicitar autorización por la fuerza, por lo que la autorización con redireccionamiento a la página final no funcionó en principio. Y en versiones anteriores a 5.0, el registro no funcionó.

All Articles