2012-06-14 17 views
10

Estoy escribiendo una aplicación de iOS usando PhoneGap (también conocido como Cordova), tengo una página de inicio de sesión html simple que registra al usuario usando XMLHttpRequest con autenticación básica sobre SSL. Todo funciona espléndidamente cuando ingresas correctamente tu nombre de usuario y contraseña. Sin embargo, si ingresas el nombre de usuario/contraseña incorrectos, nunca se llamará a ninguno de mis callbacks.iOS: Autenticación usando XMLHttpRequest - Manejo de 401 respuesta

Si ejecuta el mismo código en Chrome, por ejemplo, con el nombre de usuario/contraseña incorrectos, cromo se comporta de manera similar, excepto que aparece un cuadro de diálogo de desafío de autenticación. Pulsar cancelar en el cuadro de diálogo de Chrome devuelve el control a mi código de JavaScript. Desafortunadamente, en iOS, UIWebView ni siquiera muestra un cuadro de diálogo de autenticación, simplemente se cuelga. Necesito una forma de decirle al usuario que ingresaron el nombre de usuario o contraseña incorrectos para que puedan volver a intentarlo.

Lo más parecido a una respuesta que pude encontrar fue http://www.freelock.com/2008/06/technical-note-http-auth-with-ajax pero cambiar el estado de la respuesta del servidor no parece lo correcto. Aquí está básicamente mi código de solicitud, pero cuando se envía un mal nombre de usuario o contraseña nunca llega a mi callback (de hecho, la devolución de llamada onreadystatechange solo se llama una vez y eso es para readyState 1, también conocido como OPEN).

var req = new XMLHttpRequest(); 
req.onload = function(ev) { 
    if (req.status == 401) { 
     alert("Invalid Username/Password"); 
     document.getElementById('password').focus(); 
    } else if (req.status == 200) { 
     window.location.href = some_secure_site; 
    } else { 
     // edit // 
     alert("Some other status"); 
    } 
} 
req.onerror = function (ev) { alert('Error'); }; 
req.ontimeout = function(ev) { alert('Timeout'); }; 
req.open('GET', uri, true, userValue, passValue); 
req.withCredentials = true; 
req.send(); 
+0

Parece estar relacionado con ese problema: https://issues.apache.org/jira/browse/CB-2415 por favor vote para arreglarlo pronto – martinoss

Respuesta

3

Algunas cosas se hicieron evidentes al tratar de hacer esto en iOS. Una es que iOS tiene un error relacionado con la autenticación básica, por lo que si tu contraseña tiene ciertos caracteres especiales nunca recibirás una respuesta de tu servidor porque tu servidor nunca tendrá un desafío de autenticación. Es decir, si usa el campo de nombre de usuario y contraseña en el método "abrir".

Mi conjetura es que están haciendo algo estúpido como enviarlo por http: // nombre de usuario: [email protected]/etc cuando deberían estar utilizando cabeceras HTTP y codificación Base64 los creds al igual que

req.setRequestHeader("Authorization", "Basic " + base64(username) + ':' + base64(password)); 

La otra cosa que aprendí es que Basic Auth no es muy seguro y es propenso a un millón y un problemas. Una de las cosas que le molestará es que el cliente guardará en caché el nombre de usuario y la contraseña, lo que anulará cualquier valor nuevo que envíe a través de "req.open (...)". Buena suerte al usar Javascript solo, tendrás que hacer magia en ObjC para borrar el caché.

Si tiene control sobre su servidor, le sugiero que use la autenticación de token. Conéctese a través de SSL y luego envíe una POST con datos JSON que contengan el nombre de usuario y la contraseña. El servidor podría enviar datos JSON con un token de autenticación (esencialmente un grupo de caracteres aleatorios lo suficientemente largos como para que no puedan ser adivinados, un UUID funciona bien. Esto es generado por el servidor y solo puede ser conocido por el cliente y el servidor). Luego, almacene el token y el nombre de usuario en el llavero para que el usuario no tenga que ingresar sus credenciales cada vez que inicie su aplicación.

Mi servidor siempre enviará una respuesta de 200, pero los datos JSON contendrán la información necesaria para volver a intentar o para almacenar el token de autenticación. En general ... la autenticación básica básicamente apesta.

try { 
    var req = new XMLHttpRequest(); 
    req.onload = function(ev) { 
     var response = JSON.parse(this.responseText); 
     if (response.success === true) { 
      // The server will respond with a token that will allow us to login 
      storeCredentials(userValue, response.token); 
      // redirect with token 
     else if (req.status == 401) { 
      alert("Invalid Username/Password"); 
      document.getElementById('password').focus(); 
     } else { 
      alert("Some other status"); 
     } 
    } 
    req.ontimeout = setTimeout(function(ev) { navigator.notification.alert('Timeout trying to contact the server'); }, 10000); 
    req.onerror = function(ev) { clearTimeout(this.ontimeout); navigator.notification.alert('Error connecting to the server during authentication.'); }; 

    var uri = myWebOrigin + '/authenticate'; 
    req.open('POST', uri, true); 
    req.setRequestHeader('Cache-Control', 'no-cache'); 
    req.setRequestHeader('Content-Type', 'application/json'); 
    json_data = {username : Base64.encode(userValue), password : Base64.encode(passValue)}; 
    req.send(JSON.stringify(json_data)); 
} catch(error) { 
    navigator.notification.alert('Uh oh, an error occurred trying to login! ' + error); 
    return; 
} 
+2

Si el nombre de usuario o la contraseña contienen caracteres especiales, puede intentar llamar a encodeURIComponent (username). Tuve un problema con iOS + PhoneGap donde los nombres de usuario o contraseñas correctos que contenían estos caracteres fallarían y la codificación los solucionó. –

0

Probablemente hay otro código de estado HTTP al lado de los 401 y 200 códigos recibidos! Asegúrese de que no hay realmente ningún otro código de estado recibida:.

if (req.status == 401) { 
    alert("Invalid Username/Password"); 
    document.getElementById('password').focus(); 
} else if (req.status == 200) { 
    window.location.href = some_secure_site; 
} else { 
    alert('Unfetched status code '+req.status+' captured!'); 
} 
+0

Tengo una declaración else similar en mi código y, lamentablemente, no me sirve llamado, mi devolución de llamada onload nunca se llama. Nuevamente, el mismo comportamiento en cromo pero cromo muestra un diálogo de autenticación. cancelar el cuadro de diálogo de autenticación provocará que se active mi devolución de llamada de onload –

0

He tenido el mismo problema - ninguna de las devoluciones de llamada en la solicitud están siendo llamados si las credenciales no válidas se pasan en

Mi conjetura es que internamente se le dice a UIWebView que solicite credenciales, pero ese mensaje se está suprimiendo, lo que lleva a este error. Esto se basa en que todos los otros navegadores que he probado (Safari, Chrome, Firefox) no llaman a las devoluciones de llamada en este caso hasta que se descarta el aviso.

Otra solución (la que estoy usando) sería no usar Javascript para realizar la autenticación, y en cambio hacerlo en el lado de iOS: puede usar el código en How to display the Authentication Challenge in UIWebView? como plantilla aproximada para hacer esto.

1

Acabo de tener los mismos problemas con ninguna de las devoluciones de llamadas cuando se usa iOS + PhoneGap + jQuery. Si paso credenciales incorrectas y el uso de

$.ajax({ 
    ... 
    timeout: 5000, // Some timeout value that makes sense 
    ... 
}); 

entonces la respuesta de error se llama con {"readyState":0,"status":0,"statusText":"timeout"}. En ese caso, tendría que adivinar que el error real es el HTTP 401.

Alternativamente, puede utilizar

$.ajax({ 
    ... 
    async: false, // :-(
    ... 
}); 

y su respuesta de error obtener algo así como {"readyState":4,"responseText":"<html>...</html>","status":401,"statusText":"Unauthorized"} espalda.

+0

Esto funcionó para mí – Deviney

0

Para solucionar este problema, elimine el encabezado WWW-Authenticate de la respuesta del servidor.

Cuestiones relacionadas