Por que a solicitação de OPÇÕES de comprovação de uma solicitação de CORS autenticada funciona no Google Chrome, mas não no Firefox?

Estou escrevendo um cliente JavaScript para ser incluído em sites de terceiros (pense botão Facebook Like). Ele precisa recuperar informações de uma API que requer autenticação HTTP básica. A configuração simplificada se parece com isso:

Um site de terceiros inclui este snippet em sua página:

  

widget.js chama a 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(); 

A API foi configurada para responder com headers apropriados:

 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)?://(.+?\.[az]{3})$" AccessControlAllowOrigin=$0 Header set Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin 

Observe que o Access-Control-Allow-Origin está configurado para a Origin vez de usar um caractere curinga, porque estou enviando uma solicitação credenciada ( withCredentials ).

Agora está tudo pronto para fazer uma solicitação autenticada entre domínios e assíncrona, e ela funciona muito bem no Chrome 25 no OS X 10.8.2. No Dev Tools, posso ver a solicitação de rede para a solicitação OPTIONS antes da solicitação GET , e a resposta volta conforme o esperado.

Ao testar no Firefox 19, nenhuma solicitação de rede é exibida no Firebug para a API, e esse erro é registrado no console: NS_ERROR_DOM_BAD_URI: Access to restricted URI denied

Depois de muita escavação, descobri que o Gecko não permite que o nome de usuário e a senha fiquem diretamente em um URI entre sites, de acordo com os comentários. Eu assumi que isso era de usar o usuário opcional e senha params para open() então eu tentei o outro método de fazer pedidos autenticados que é para codificar as credenciais Base64 e enviar um header de autorização:

 // Base64 from http://www.webtoolkit.info/javascript-base64.html auth = "Basic " + Base64.encode(user + ":" + pass); ... // after open() and before send() httpRequest.setRequestHeader('Authorization', auth); 

Isso resulta em uma resposta 401 Unauthorized à solicitação OPTIONS que leva a pesquisas no Google, como “Por que isso funciona no Chrome e não no Firefox !?” Foi quando eu soube que estava com problemas.

Por que funciona no Chrome e não no Firefox? Como posso obter a solicitação OPTIONS para enviar e responder de forma consistente?

Por que funciona no Chrome e não no Firefox?

A especificação W3 para solicitações de preflight do CORS afirma claramente que as credenciais do usuário devem ser excluídas. Há um bug no Chrome e no WebKit, onde as solicitações OPTIONS retornam um status 401 ainda enviam a solicitação subsequente.

O Firefox tem um bug relacionado arquivado que termina com um link para a lista de discussão de aplicativos da Web do W3 solicitando que as especificações do CORS sejam alteradas para permitir que os headers de autenticação sejam enviados na solicitação OPTIONS em benefício dos usuários do IIS. Basicamente, eles estão esperando por esses servidores ficarem obsoletos.

Como posso obter a solicitação OPTIONS para enviar e responder de forma consistente?

Basta que o servidor (API neste exemplo) responda às solicitações OPTIONS sem exigir autenticação.

Kinvey fez um bom trabalho de expansão sobre isso, ao mesmo tempo em que ligava a uma questão da API do Twitter descrevendo curiosamente o problema do catch-22 deste cenário exato algumas semanas antes de qualquer um dos problemas do navegador ser arquivado.

Este é um post antigo, mas talvez isso possa ajudar as pessoas a completar o problema do CORS. Para concluir o problema básico de autorização, você deve evitar a autorização para solicitações OPTIONS em seu servidor. Este é um exemplo de configuração do Apache. Basta adicionar algo assim em seu VirtualHost ou Location.

  AuthType Basic AuthName  Require valid-user AuthUserFile