2013-03-31 3 views
43

Sto scrivendo un client JavaScript da includere in siti di terze parti (si pensi al pulsante Mi piace di Facebook). È necessario recuperare le informazioni da un'API che richiede l'autenticazione HTTP di base. La configurazione semplificata è simile al seguente:Perché la richiesta OPZIONI di verifica preliminare di una richiesta CORS autenticata funziona in Chrome ma non in Firefox?

Un sito 3rd party include questo frammento sulla loro pagina:

<script 
async="true" 
id="web-dev-widget" 
data-public-key="pUbl1c_ap1k3y" 
src="http://web.dev/widget.js"> 
</script> 

widget.js chiama l'API:

var el = document.getElementById('web-dev-widget'), 
    user = 'token', 
    pass = el.getAttribute('data-public-key'), 
    url = 'https://api.dev/', 
    httpRequest = new XMLHttpRequest(), 
    handler = function() { 
     if (httpRequest.readyState === 4) { 
     if (httpRequest.status === 200) { 
      console.log(httpRequest.responseText); 
     } else { 
      console.log('There was a problem with the request.', httpRequest); 
     } 
     } 
    }; 

httpRequest.open('GET', url, true, user, pass); 
httpRequest.onreadystatechange = handler; 
httpRequest.withCredentials = true; 
httpRequest.send(); 

L'API è stata configurata per rispondere con intestazioni appropriate:

Header set Access-Control-Allow-Credentials: true 
Header set Access-Control-Allow-Methods: "GET, OPTIONS" 
Header set Access-Control-Allow-Headers: "origin, authorization, accept" 
SetEnvIf Origin "http(s)?://(.+?\.[a-z]{3})$" AccessControlAllowOrigin=$0 
Header set Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin 

Si noti che Access-Control-Allow-Origin è impostato su Origin invece di utilizzare un carattere jolly perché sto inviando una richiesta con credenziali (withCredentials).

Ora è tutto pronto per creare una richiesta autenticata da più domini asincroni e funziona perfettamente su Chrome 25 su OS X 10.8.2. In Dev Tools, posso vedere la richiesta di rete per la richiesta OPTIONS prima della richiesta GET e la risposta ritorna come previsto.

Quando si verifica in Firefox 19, nessuna richiesta di rete appaiono in Firebug alle API, e questo errore viene registrato nella console: NS_ERROR_DOM_BAD_URI: Access to restricted URI denied

Dopo molte scavo, ho scoperto che Gecko doesn't allow the username and password to be directly in a cross-site URI, secondo le osservazioni. Ho pensato che questo era l'uso del utente e la password opzionale params per open() così ho provato l'altro metodo di fare le richieste autenticate che è quello di Base64 codificare le credenziali e inviare in un'intestazione Autorizzazione:

// Base64 from http://www.webtoolkit.info/javascript-base64.html 
auth = "Basic " + Base64.encode(user + ":" + pass); 

... 
// after open() and before send() 
httpRequest.setRequestHeader('Authorization', auth); 

Questo si traduce in un 401 Unauthorized risposta alla richiesta OPTIONS che conduce a ricerche di Google come "Perché funziona in Chrome e non in Firefox !?" Fu allora che capii di essere nei guai.

Perché fa funziona in Chrome e non in Firefox? Come posso ottenere la richiesta OPTIONS per inviare e rispondere in modo coerente?

+0

Mi piacerebbe inserire come rendere la domanda migliore – maxbeatty

risposta

100

Perché fa funziona in Chrome e non in Firefox?

Il W3 spec for CORS preflight requests indica chiaramente che le credenziali dell'utente devono essere escluse. C'è un bug in Chrome e WebKit dove le richieste OPTIONS che restituiscono uno stato di 401 inviano ancora la richiesta successiva.

Firefox ha un bug legato depositato che si conclude con un link al W3 public webapps mailing list chiedendo la spec CORS essere cambiato per consentire intestazioni di autenticazione da inviare su richiesta OPTIONS a beneficio degli utenti di IIS. Fondamentalmente, stanno aspettando che quei server siano obsoleti.

Come posso ottenere la richiesta OPTIONS per inviare e rispondere in modo coerente?

Basta che il server (API in questo esempio) risponda alle richieste OPTIONS senza richiedere l'autenticazione.

Kinvey ha fatto un buon lavoro di espansione su questo mentre collegava anche a an issue of the Twitter API delineando il problema di catch-22 di questo esatto scenario interessante un paio di settimane prima che qualsiasi problema del browser fosse archiviato.

+1

Devo fornire sempre la stessa risposta alla richiesta OPZIONI o dovrebbe dipendere dalla risorsa richiesta? Se dipende dalla risorsa, l'utente malintenzionato può utilizzare la richiesta OPTIONS per scoprire il contenuto/gli URL del server e le funzionalità supportate da tali risorse. Consulta anche questa domanda: http://stackoverflow.com/questions/20805058/options-request-authentication –

+11

Esiste un rischio per la sicurezza di non autenticare le richieste OPTION? –

+10

Per quelli che finiscono qui: vale la pena usare 'curl -X OPTIONS http: // yourdomain.example.com' per vedere a cosa risponde il tuo server per una richiesta OPTIONS. Assicurati inoltre di cancellare completamente la cache del browser, Firefox memorizza in modo aggressivo queste risposte, quindi anche se hai modificato la configurazione del server, continua a riportare gli stessi errori. – toxaq