2012-06-14 6 views
10

Sto scrivendo un'applicazione iOS utilizzando PhoneGap (ovvero Cordova), ho una semplice pagina di accesso html che registra l'utente utilizzando una XMLHttpRequest con autenticazione di base su SSL. Tutto funziona splendidamente quando inserisci il nome utente e la password correttamente. Tuttavia, se si immette il nome utente/password errato, nessuno dei miei callback viene mai chiamato.iOS: autenticazione tramite XMLHttpRequest - Gestione della risposta 401

Se si esegue lo stesso codice su Chrome, ad esempio, con nome utente/password errati, chrome si comporta in modo simile, tranne che apre una finestra di dialogo di verifica autenticazione. Colpire cancel sulla finestra di chrome restituisce il controllo al mio codice javascript. Sfortunatamente, su iOS, UIWebView non visualizza nemmeno una finestra di dialogo di autenticazione, ma semplicemente si blocca. Ho bisogno di un modo per dire all'utente che hanno inserito il nome utente o la password sbagliati in modo che possano riprovare.

La cosa più vicina a una risposta che ho trovato è stata questa http://www.freelock.com/2008/06/technical-note-http-auth-with-ajax ma cambiare lo stato di risposta dal server non sembra la cosa giusta da fare.

Ecco come appare il mio codice di richiesta, ma quando viene inviato un nome utente o una password errati non raggiunge mai il mio callback onload (infatti il ​​callback onreadystatechange viene chiamato una sola volta e questo è per readyState 1, ovvero 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

Sembra essere correlato a questo problema: https://issues.apache.org/jira/browse/CB-2415 si prega di votare per farlo riparare presto – martinoss

risposta

3

Alcune cose sono diventate evidenti per me mentre cercavo di farlo su iOS. Uno è che iOS ha un bug relativo all'autenticazione di base, quindi se la tua password contiene determinati caratteri speciali non riceverai mai una risposta dal tuo server perché il tuo server non otterrà mai una sfida di autenticazione. Cioè, se stai usando il nome utente e il campo password nel metodo "aperto".

La mia ipotesi è che stanno facendo qualcosa di stupido come inviandola tramite http: // nomeutente: [email protected]/etc quando dovrebbero essere utilizzando le intestazioni HTTP e la codifica Base64 le creds in questo modo

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

L'altra cosa che ho imparato è che Basic Auth non è molto sicuro ed è soggetto a un milione di problemi. Uno di quelli che ti infastidiranno è che il client memorizzerà nella cache il nome utente e la password, che sostituiranno tutti i nuovi valori inviati tramite "req.open (...)". Buona fortuna per evitare che usando javascript da solo, dovrai fare un po 'di magia in ObjC per svuotare la cache.

Se si ha il controllo sul proprio server, suggerirei di utilizzare l'autenticazione token. Connettersi su SSL e quindi inviare un POST con dati JSON contenenti nome utente e password. Il server potrebbe quindi restituire i dati JSON con un token di autenticazione (essenzialmente un gruppo di caratteri casuali abbastanza a lungo da non poter essere indovinato, un UUID funziona bene, generato dal server e può essere conosciuto solo dal client e il server). Quindi archivia il token e il nome utente nel portachiavi in ​​modo che l'utente non debba inserire i propri dati ogni volta che avvia l'app.

Il mio server invierà sempre una risposta di 200 ma i dati JSON conterranno le informazioni necessarie per riprovare o per memorizzare il token di autenticazione. In generale ... l'autenticazione di base fa schifo.

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

Se il nome utente o la password contengono caratteri speciali, è possibile provare a chiamare encodeURIComponent (nome utente). Ho avuto un problema con iOS + PhoneGap in cui i nomi utente o le password corretti contenenti questi caratteri non sarebbero riusciti e la codifica l'ha risolto. –

0

Probabilmente c'è un altro codice di stato HTTP accanto alle 401 e 200 codici ricevuti! Assicurarsi che non c'è davvero nessun altro codice di stato ricevuto:.

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

Ho una dichiarazione simile nel mio codice e purtroppo non ottiene chiamato, il mio callback onload non viene mai chiamato. Di nuovo, lo stesso comportamento in chrome ma chrome mostra una finestra di dialogo auth. l'annullamento della finestra di dialogo dell'autorizzazione provocherà l'attivazione della richiamata onload –

0

Ho avuto lo stesso problema - nessuno dei callback sulla richiesta sono chiamati se credenziali non valide sono passati in

La mia ipotesi è che internamente a UIWebView viene richiesto di richiedere le credenziali, ma tale richiesta viene soppressa, portando a questo bug. Questo è basato su tutti gli altri browser che ho provato (Safari, Chrome, Firefox) che non chiamano i callback in questo caso fino a quando il prompt non viene chiuso.

Un'altra soluzione (quella che sto usando) sarebbe quella di non usare Javascript per fare l'autenticazione, e invece farlo sul lato iOS - è possibile utilizzare il codice su How to display the Authentication Challenge in UIWebView? come modello approssimativo per farlo.

1

Ho appena avuto gli stessi problemi con nessuno dei callback che vengono chiamati quando si utilizza iOS + PhoneGap + jQuery. Se mi passa credenziali errate e utilizzare

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

poi il callback di errore viene chiamato con {"readyState":0,"status":0,"statusText":"timeout"}. In questo caso si dovrà indovinare che il vero errore è il HTTP 401.

In alternativa è possibile utilizzare

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

e il callback di errore verrà ottenere qualcosa di simile {"readyState":4,"responseText":"<html>...</html>","status":401,"statusText":"Unauthorized"} indietro.

+0

Questo ha funzionato per me – Deviney

0

Per risolvere questo problema, rimuovere l'intestazione WWW-Authenticate dalla risposta del server.