Why is my Drupal 8 CORS program not working?
Since Drupal 8.2, the cors installation is in the core. In my services.yml
(s default.services.yml
) I have the following setup:
cors.config:
enabled: true
# Specify allowed headers, like 'x-allowed-header'.
allowedHeaders: ['x-csrf-token','authorization','content-type','accept','origin','x-requested-with']
# Specify allowed request methods, specify ['*'] to allow all possible ones.
allowedMethods: ['*']
# Configure requests allowed from specific origins.
allowedOrigins: ['*']
# Sets the Access-Control-Expose-Headers header.
exposedHeaders: false
# Sets the Access-Control-Max-Age header.
maxAge: 1000
# Sets the Access-Control-Allow-Credentials header.
supportsCredentials: true
My domain a.com
is password protected by htaccess.
On a domain b.com
I am trying to load some API from a domain a.com
:
$.ajaxSetup({
xhrField: {
withCredentials : true
},
beforeSend: function (xhr) {
xhr.setRequestHeader('Authorization', 'Basic Z2VuaXVzOmNvYXRpbmdz');
}
});
request = $.ajax({
url: apiBaseUrl + 'api/foobar',
dataType: 'json',
type: 'get',
password: 'foo',
username: 'bar'
});
In chrome it works fine, in firefox I get an error. Request headers:
Access-Control-Request-Method: GET
Access-Control-Request-Headers: authorization
A 401 response "Authorization required", it says the request method is OPTIONS (?).
What's wrong here?
Doing the same query in insomnia works great.
source to share
A 401 response "Authorization required", it says the request method is OPTIONS (?).
You need to set up a backend server to not require authorization for requests OPTIONS
.
This 401 response indicates that the server needs authorization for the request OPTIONS
, but authorization fails because the request does not contain the required credentials.
This is because the request is OPTIONS
sent automatically by your browser as part of CORS.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Preflighted_requests explains why browsers make CORS preflight requests OPTIONS
, but in the specific case in question the reason is because the request contains a request header Authorization
.
So what's going on:
- Your codes telling your browser want to send a request with a header
Authorization
. - Your browser says "OK, header requests
Authorization
require me to do a CORS previewOPTIONS
for the server to allow header requestsAuthorization
." - Your browser is sending a request to the
OPTIONS
server without a headerAuthorization
, since the whole purpose of validationOPTIONS
is to see if it's OK to include that header. - Your server sees the request
OPTIONS
, but instead of responding to it with a headerAuthorization
in the requests, it rejects it with a 401 because it is missing a header. - Your browser expects a 200 or 204 response for CORS preflight, but instead gets a 401 response. This way your browser stops right there and never tries the request
GET
from your code.
So, you need to figure out what part of your current server-side code is causing your server to request authorization for requests OPTIONS
, and you need to change that to handle requests OPTIONS
without the need for authorization instead .
Once you fix this, the existing cors.config
one listed in the question should force the server to respond to the request in the OPTIONS
correct way - with a response header Access-Control-Allow-Headers
that includes Authorization
, so your browser can say "OK", this server allows cross-origin requests containing the header Authorization
. so now I go and post the actual request GET
.
source to share
In addition to working CORS configuration and htaccess setup:
SetEnvIfNoCase Request_Method OPTIONS noauth
Order Deny,Allow
Deny from all
Require valid-user
Allow from env=noauth
Satisfy Any
you have to make sure your ajax setup doesn't have withCredentials
u password
and u username
like in my question. This leads to errors in Firefox, at least. Working ajax:
$.ajaxSetup({
xhrField: {
withCredentials : true
},
beforeSend: function (xhr) {
xhr.setRequestHeader('Authorization', 'Basic foobarbarfoo');
}
});
request = $.ajax({
url: isApiActive ? apiBaseUrl + 'api/mydata' : './mydata.json',
dataType: 'json',
type: 'get'
});
source to share