Я пишу JavaScript-клиент, который будет включен на сторонние сайты (подумайте о кнопке Facebook Like). Он должен извлекать информацию из API, для которой требуется базовая HTTP-аутентификация. Упрощенная установка выглядит следующим образом:
Сайт сторонних разработчиков включает этот фрагмент на своей странице:
<script
async="true"
id="web-dev-widget"
data-public-key="pUbl1c_ap1k3y"
src="http://web.dev/widget.js">
</script>
widget.js вызывает 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();
API был настроен на ответ с соответствующими заголовками:
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
Обратите внимание, что параметр Access-Control-Allow-Origin
установлен в Origin
вместо того, чтобы использовать подстановочный знак, потому что я отправляю запрос с подтверждением (withCredentials
).
Теперь все работает, чтобы выполнить асинхронный междоменный аутентифицированный запрос, и он отлично работает в Chrome 25 на OS X 10.8.2. В Dev Tools я вижу запрос сети для запроса OPTIONS
перед запросом GET
, и ответ возвращается, как ожидалось.
При тестировании в Firefox 19 в Firebug API не появляются сетевые запросы, и эта ошибка регистрируется в консоли: NS_ERROR_DOM_BAD_URI: Access to restricted URI denied
После многократного поиска я обнаружил, что Gecko не позволяет пользователю и паролю быть непосредственно в межсайтовом URI в соответствии с комментариями. Я предположил, что это связано с использованием необязательных параметров пользователя и пароля для open()
, поэтому я попробовал другой способ сделать аутентифицированные запросы, которые для Base64 кодируют учетные данные и отправляют в заголовок авторизации:
// Base64 from http://www.webtoolkit.info/javascript-base64.html
auth = "Basic " + Base64.encode(user + ":" + pass);
...
// after open() and before send()
httpRequest.setRequestHeader('Authorization', auth);
Это приводит к ответе 401 Unauthorized
на запрос OPTIONS
, который приводит к поисковым запросам Google, например: "Почему это работает в Chrome, а не в Firefox!?" Это, когда я знал, что у меня проблемы.
Почему он работает в Chrome, а не в Firefox? Как я могу получить запрос OPTIONS
для отправки и ответа последовательно?