The following code (using the PouchDB Authentication plugin) fails because it triggers the browser to send a CORS preflight request, and CouchDB does not support the OPTIONS HTTP method.
var db = new PouchDB("http://localhost:5984/mydb");
db.login('username', 'password');
// assume the database URL and login info are valid
Here is the error (in Chrome). Note that this issue also occurs in Edge, but not in Firefox:
XMLHttpRequest cannot load http://localhost:5984/_session. Response for preflight has invalid HTTP status code 405
And here are the headers that Chrome is sending for the request (they are not significantly different in Firefox):
POST /_session HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Content-Length: 25
Accept: application/json
Origin: http://localhost:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Referer: http://localhost:8080/
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.8,es-419;q=0.6,es;q=0.4
I have already enabled CORS via the add-cors-to-couchdb Node script. Things I have tried:
Manually adding OPTIONS as a method under [cors] in my local.ini
Passing { ajax: { content_type: "text/plain" } } as the third argument to login
So, my question is:
How can I prevent the preflight request from being triggered? Looking at the MDN documentation, it doesn't seem necessary.
If the previous is not possible, how can I set my CouchDB server up to respond to preflight requests?
Hitting this same issue. Seems Chrome has recently started being a little more aggressive about sending the OPTIONS preflight.
A partial work around was to specify a specific origin in the CORS header instead of '*',
so
curl -X PUT $HOST/_config/cors/origins -d '"localhost:8080"'
or similar.
I still am getting the preflight error, but now PouchDB successfully authenticates, so I can just ignore the error.
I think the fix is to get CouchDB to respond to OPTIONS on the _session url.
Edit, more info here
https://github.com/nolanlawson/pouchdb-authentication/issues/111
With the latest PouchDB you authenticate like so:
var remote = new PouchDB("http://user:password#localhost:4984/bucket/");
Related
I am trying to understand how the whole CORS policy works. To explain my confusion, let me give you an example:
$.get("https://www.google.com", function(response) { alert(response) });
The above request will return with the following error:
XMLHttpRequest cannot load https://www.google.com/. Redirect from 'https://www.google.com/' to 'https://www.google.ca/?gfe_rd=cr&ei=TlqUWeGEH5HRXqW6utgI' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://fiddle.jshell.net' is therefore not allowed access.
Now in order for that to work, google would have to white-list https://fiddle.jshell.net.
Now, if I were to try the same thing on a restful API page, that will work. My question is really simple, Why?
Trying to analyze this, I tried hitting an API and analyzing its response:
https://apigee.com/console/bing?req=%7B%22resource%22%3A%22web_search%22%2C%22params%22%3A%7B%22query%22%3A%7B%22query%22%3A%22sushi%22%2C%22sources%22%3A%22web%22%7D%2C%22template%22%3A%7B%22format%22%3A%22json%22%7D%2C%22headers%22%3A%7B%7D%2C%22body%22%3A%7B%22attachmentFormat%22%3A%22mime%22%2C%22attachmentContentDisposition%22%3A%22form-data%22%7D%7D%2C%22verb%22%3A%22get%22%7D
Response:
HTTP/1.1 200
Date:
Wed, 16 Aug 2017 14:31:32 GMT
Content-Length: 266
Connection: keep-alive
Content-Type: application/json; charset=utf-8
Server: Apigee Router
X-Content-Type-Options: nosniff
I came to conclusion that it must be the headers. Specifically I belive that it is this header: Content-Type: application/json; But I don't know for sure, I am trying to understand this and hoping somebody here can explain to me.
So I did 2 tests: running your code $.get("https://www.google.com", function(response) { alert(response) }); snippet from the console and requesting https://www.google.com from https://apigee.com/console/others
I think what happens in the 1st case is the fact that the request is done from the client, next request headers are sent:
:authority:www.google.com
:method:GET
:path:/?_=1502896196820
:scheme:https
accept:*/*
accept-encoding:gzip, deflate, br
accept-language:en-US,en;q=0.8
origin:https://stackoverflow.com
referer:https://stackoverflow.com/questions/45717044/understanding-page-response
user-agent:Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3187.0 Safari/537.36
x-chrome-uma-enabled:1
x-client-data:CJG2yQEIo7bJAQiMmMoBCKudygEIs53KAQjRncoBCKiiygE=
Since Google does not reply with 'Access-Control-Allow-Origin: *' - client, and in the request I have origin:https://stackoverflow.com, Chrome in my case throws CORS error.
In the 2nd test, using https://apigee.com/console/others and requesting https://www.google.com , apigee.com seems to overwrite headers and sends:
GET / HTTP/1.1
Host:
www.google.com
X-Target-URI:
https://www.google.com
Connection:
Keep-Alive
Also, from DEV console, I can see it does server to server call so no client involved in throwing CORS, thus I am getting the responses with Google page.
UPDATE:
Regarding JSON API requests, here is some interesting info from Google CloudPlatform about CORS
Note: CORS configuration applies only to XML API requests. For JSON
API requests, Cloud Storage returns the Access-Control-Allow-Origin
header with the origin of the request.
Thus, if the request is performed from the client, a client should not throw CORS errors since it gets Access-Control-Allow-Origin with the same origin it sent.
However, different APIs and clients might process requests differently. Thus, sometimes Firefox throws CORS while Chrome does not.
I am trying to add a custom header to my angular js GET request as below:
$http({
method : 'GET',
url : s,
headers : {
"partnerId" : 221,
"partnerKey" : "heeHBcntCKZwVsQo"
}
})
But the issue is the headers are getting added to Access-Control-Request-Headers as below and I am getting 403 Forbidden response:
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:50.0)
Gecko/20100101 Firefox/50.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/ *;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Access-Control-Request-Method: GET
Access-Control-Request-Headers: partnerid,partnerkey
Origin: http://localhost:8080
DNT: 1
Connection: keep-alive
I also tried below changes but no luck
return $http.get(s, {
headers : {
'partnerId' : 221,
'partnerKey': 'heeHBcntCKZwVsQo'
}
})
In other related answers in SO I saw that the header partnerId and partnerKey need to be enabled in server side. But I am able to add these custom headers in POSTMAN client and other POST clients and able to get the expected response. So I guess I am missing something. Can someone guide me in this. Thanks in advance
Edit: One more thing I noted is that partnerId is replaced as partnerid while passing in the request. Not sure if that makes a difference.
If you add any headers to a scripted cross-origin request other than any CORS-safelisted request-headers, it triggers browsers to first do a CORS preflight request.
There is no way to prevent users’ browsers from doing that CORS preflight (though there are ways to get around it locally in your own browser when doing testing; for example, by using Postman).
So for users to be able to use a Web app of yours that makes scripted cross-origin requests with custom headers, the server to which those cross-origin requests go needs to be CORS-aware.
The reason Postman can make such requests without causing a preflight is, Postman’s not a browser engine—it's an extension that’s not restricted by the same-origin policy, so doesn’t need CORS.
Postman can basically do whatever curl or other such tools can do, but just within a browser UI for convenience. It’s otherwise bypassing normal Web-security features built into browsers.
I got this code:
var req = new HttpRequest();
req.open("POST", "http://localhost:8031/rest/user/insert");
req.setRequestHeader("Content-type", "application/json");
req.send(json.stringify(user_map));
But, instead of sending the POST verb, when I see it in fiddler I see this:
OPTIONS http://localhost:8031/rest/user/insert HTTP/1.1
Host: localhost:8031
Connection: keep-alive
Access-Control-Request-Method: POST
Origin: http://127.0.0.1:3030
User-Agent: Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.33 (KHTML, like Gecko) Chrome/27.0.1430.0 (Dart) Safari/537.33
Access-Control-Request-Headers: origin, content-type
Accept: */*
Referer: http://127.0.0.1:3030/E:/grole/dart/Clases/Clases/web/out/clases.html
Accept-Encoding: gzip,deflate,sdch
Accept-Language: es-ES,es;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
As you can see, it is using the OPTIONS verb instead of POST?
What's going on?
The OPTIONS verb is a preflight request sent by some browsers to check the validity of cross origin requests. It pretty much checks with the server that the Origin(requester) is allowed to make the request for a specified resource. Also, depending on which headers are sent back by the server it lets the browser know which headers, methods, and resources the origin is allowed to request form the server.
The browser sends the OPTIONS request then if the server answers back with the correct headers (CORS headers) allowing the origin to make the request, you should see your POST request go through afterwards.
Note that the CORS headers must be returned on both the OPTIONS response as well as the POST response. This means your server must be able to respond to the options method on the routes you want to access across domains.
This is known as Cross-origin Resource Sharing. Mozilla has some pretty good documentation on the subject. https://developer.mozilla.org/en-US/docs/HTTP/Access_control_CORS
If you have more questions let me know and I'll answer them.
One way to avoid this problem is by sending the request payload without custom headers and using formData to setup your request payload.
This question already has answers here:
Why am I getting an OPTIONS request instead of a GET request?
(10 answers)
Closed 6 years ago.
I am trying to make basic request to a working server (checked through google POSTMAN) in an emberJS application. But when I try it, I get http 404 error. Then I inspected the error, and saw that the original request which comes from client side came as OPTION request instead of GET request. Also, I've set two header, Accept and Content-Type to be application/json and when inspecting, inspector printed these headers:
Accept:*/*
Accept-Encoding:gzip, deflate, sdch
Accept-Language:en-US,en;q=0.8
Access-Control-Request-Headers:accept, content-type
Access-Control-Request-Method:GET
Connection:keep-alive
Host:frontend.com
Origin:http://localhost:4200
Referer:http://localhost:4200/restaurant
User-Agent:Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.110 Safari/537.36
Now here is the adapter
//Application.js adapter:
import DS from 'ember-data';
export default DS.JSONAPIadapter.extend({
host: 'http://server.com',
namespace: 'v1',
headers: {
"Accept": "application/json",
"Content-Type": "application/json"
}
});
Is there some way to turn this OPTION off, or is there some workaround? I didn't believe ember was so buggy, because these is supposed to be one of main things that emberJS should do - send requests.
Any help is appreaciated.
This is "request preflight". While doing a cross origin request, browser first sends a preflight request by sending an OPTION request. If the OPTION request is responded successfully, the real request would be send to the server.
The "preflight" fails in such cases:
CORS is not enabled by the server
authentication / authorization problems are occured
service function is not found (404)
service function do not accept the header you will send
You may search with these keywords: preflight, cors
Vitals:
Chrome Version 23.0.1271.64
Mac OS X 10.8.2
The Situation:
I have a web service exposed at https://api.myapi.com/...
I would like to allow JavaScript applications to interact with this API. CORS feels like the obvious best fit (jsonp and friends being terrible, terrible hacks).
I can see in my Developer Tools that preflight requests are being sent out:
Request URL: https://api.myapi.com/someapi
Request Method: OPTIONS
Status Code: 200 OK
Request Headers
Accept: */*
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Access-Control-Request-Headers: origin, authorization, accept
Access-Control-Request-Method: GET
Connection: keep-alive
Host: api.myapi.com
Origin: http://prototypes.mycompany.com
Referer: http://prototypes.mycompany.com/somepage.html
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11
Response Headers
Access-Control-Allow-Headers: Accept,Authorization,Content-Length,Content-Type,Cookie
Access-Control-Allow-Methods: GET,POST,PUT,DELETE,HEAD,OPTIONS
Access-Control-Allow-Origin: http://prototypes.mycompany.com
Access-Control-Allow-Credentials: true
Connection: close
This looks great, in particular the response Access-Control-Allow-Origin header seems to match the request Origin header precisely.
Immediately afterwards, the actual request is attempted:
Request URL: https://api.myapi.com/someapi
Request Headers
Accept: application/json, text/javascript, */*; q=0.01
Authorization: <custom authentication scheme, removed>
Origin: http://prototypes.mycompany.com
Referer: http://prototypes.mycompany.com/somepage.html
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11
It then shows up as "Cancelled" and the following error is emitted:
XMLHttpRequest cannot load https://api.myapi.com/someapi. Origin http://prototypes.mycompany.com is not allowed by Access-Control-Allow-Origin.
I originally started with * as my allowed origin, but that fails with precisely the same problem.
I have replicated this problem on Mobile Safari for iOS 5, and Firefox as well. In Firefox the behavior is even more confusing:
The server responds 200 OK and then 6-7KB of data (which is what I expect). The client side log says that it got a 200 OK and ~300 bytes of data (with no response headers, no response at all...) and then triggers the "error" handler.
What am I missing?
Problem solved! I had to break out gdb to figure it out though...
A proxy server was inserting its own CORS headers into the request. So the HTTP session had the headers specified multiple times. The security check was checking against the first header, which was "*", and fails the request. The request failure prints out the last header value, which looks correct.
Not very helpful, WebKit!
Ideas:
I notice you don't have Origin in your Access-Control-Allow-Headers. I wouldn't think they'd be necessary, but you have Content-Length and I wouldn't think that'd be necessary either.
You're using Authorization, do you think you're supposed to use credentials? (Apparently sometimes you're supposed to include Access-Control-Allow-Credentials: true in the response.)
(These barely merit an "answer" but the stupid comments don't allow newlines.)