Почему предварительный запрос OPTIONS аутентифицированного запроса CORS работает в Chrome, но не в Firefox?
Я пишу 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
запрос отправить и ответить последовательно?
4 ответа
Почему это работает в Chrome, а не в Firefox?
В спецификации W3 для предварительных запросов CORS четко указано, что учетные данные пользователя должны быть исключены. В Chrome и WebKit есть ошибка, из-за которой OPTIONS
запросы, возвращающие статус 401, все еще отправляют последующий запрос.
В Firefox есть связанная ошибка, которая заканчивается ссылкой на список рассылки общедоступных веб-приложений W3, в котором требуется изменить спецификацию CORS, чтобы разрешить отправку заголовков аутентификации на OPTIONS
запрос в пользу пользователей IIS. По сути, они ждут, чтобы эти серверы были устаревшими.
Как я могу получить OPTIONS
запрос отправить и ответить последовательно?
Просто попросите сервер (API в этом примере) ответить на OPTIONS
запросы без аутентификации.
Кинви проделал хорошую работу, подробно остановившись на этом, а также связался с проблемой API-интерфейса Twitter, в которой интересно описал проблему catch-22 этого точного сценария за пару недель до того, как были поданы какие-либо проблемы с браузером.
Это старый пост, но, возможно, это поможет людям решить проблему с CORS. Для завершения основной проблемы авторизации вам следует избегать авторизации запросов OPTIONS на вашем сервере. Это пример конфигурации Apache. Просто добавьте что-то подобное в ваш VirtualHost или Location.
<LimitExcept OPTIONS>
AuthType Basic
AuthName <AUTH_NAME>
Require valid-user
AuthUserFile <FILE_PATH>
</LimitExcept>
Это было особенно для меня. Я отправляю заголовок с именем 'SESSIONHASH'. Нет проблем для Chrome и Opera, но Firefox также хочет, чтобы этот заголовок был в списке "Access-Control-Allow-Headers". В противном случае Firefox выдаст ошибку CORS.
Для справки: причина хорошо объяснена в другом ответе , а Firefox 87+ предоставляет обходной путь, как первоначально объяснялось здесь .
Начиная с Firefox 87 (выпущенного в марте 2021 г.), в about:config можно установить указанные ниже настройки , а именно в редакторе конфигурации Firefox:
network.cors_preflight.allow_client_cert: true
Из :
Корпорации, использующие клиентские сертификаты TLS, могут перевернуть
network.cors_preflight.allow_client_cert
предпочтение получить совместимую с Google Chrome обработку протокола CORS. В частности, в отличие от стандарта Fetch, при этом сертификаты клиента TLS будут передаваться вместе с предварительной отправкой CORS. Дополнительную информацию см. в