15

I try to POST from my angular login service:

$http.post('https://xyz/login',
            {
                headers: {
                    'Content-type': 'application/json',
                    'Accept': 'application/json',
                    'signature': 'asd'
                }

And I get this error:

XMLHttpRequest cannot load https://xyz/login. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:1337' is therefore not allowed access.

I tried this headers:

$httpProvider.defaults.useXDomain = true;
delete $httpProvider.defaults.headers.common['X-Requested-With'];

And also these:

"Access-Control-Allow-Origin": "*";
"Access-Control-Allow-Headers": "X-Requested-With";
"Access-Control-Allow-Methods": "GET, POST", "PUT", "DELETE";

The interesting thing, is that the POSTMAN works. What shoud I have to do?

Thanks.

georgeawg
  • 46,994
  • 13
  • 63
  • 85
user3712353
  • 2,741
  • 4
  • 13
  • 31
  • do you have CORS enabled on your server? please refer to http://enable-cors.org/ for details – emran Dec 23 '15 at 15:56
  • Hey. It's not my server, it's an external service. I guess cors are enabled because I can get a response by POSTMAN – user3712353 Dec 23 '15 at 16:40
  • 1
    POSTMAN does not run a normal Web context and is not subject to the same-origin policy. See [How does Same Origin Policy apply to browser extensions?](http://stackoverflow.com/questions/11849945/how-does-same-origin-policy-apply-to-browser-extensions/11850557#11850557) – apsillers Dec 23 '15 at 16:45

3 Answers3

8

Your request includes non-simple headers Content-type and signature which must be included in the response's Access-Control-Allow-Headers header.

(Content-type is sometimes a simple header, but only for particular values. application/json is not one of those values, and it causes Content-type to become non-simple.)

Add Content-type to Access-Control-Allow-Headers in your server's preflight response.

POSTMAN is not bound by the same-origin policy, so it does not require CORS support from the server.

apsillers
  • 101,930
  • 15
  • 206
  • 224
  • interesting thanks for this insight, in this case is an entry for signature needed also? – danday74 Dec 23 '15 at 16:17
  • Still the same. I added this to my angular app config: `$httpProvider.defaults.useXDomain = true; delete $httpProvider.defaults.headers.common['X-Requested-With']; $httpProvider.defaults.headers.common['Access-Control-Allow-Headers'] = '*'; $httpProvider.defaults.headers.common['Access-Control-Allow-Origin'] = '*'; $httpProvider.defaults.headers.common['Access-Control-Allow-Methods'] = '*';` – user3712353 Dec 23 '15 at 16:19
  • @user3712353 Oh, I assumed the later code snippets were running on your server-side code. You need to have the *server* send correct CORS headers. – apsillers Dec 23 '15 at 16:35
  • Hwy. No, it's in my angular config. The server is not mine. – user3712353 Dec 23 '15 at 16:50
3

Is your browser making an OPTIONS request before POSTing? check the NET tab I've had issues before where an OPTIONS request was being made by the browser or Angular (don't know which) and the server did not have ...

"Access-Control-Allow-Methods": "GET, POST", "PUT", "DELETE", "OPTIONS";

danday74
  • 38,089
  • 29
  • 169
  • 214
  • Hey. As you can see, I added '*' for the Methods. It still not working. – user3712353 Dec 23 '15 at 16:24
  • OK in your node code dump the headers that POSTMAN is using and dump the headers used by the browsers and compare the two and try and align them to the config that is working! I would get the Angular headers and use them in Postman too and see if it fails. Find out which header is causing the problem by removing them one at a time. You could just get the headers from the browsers NET tab and then copy them over to the POSTMAN request then start removing them one at a time – danday74 Dec 23 '15 at 16:26
  • The service I POST to is not mine, it's an external service for authentication. I dont know what should I change in the angular code, and yes, it sends OPTIONS as method – user3712353 Dec 23 '15 at 16:30
  • OK the OPTIONS request is made by Angular or the browser automatically when a cross domain request is made - it is not made by you. The OPTIONS request is trying to find out if the CORS headers are sufficient for you to make a POST. The problem is probably that because OPTIONS seems to be a new thing, the server you want to POST too prob does not support OPTIONS! I believe there may be a way to prevent Angular from making an OPTIONS request but I'm not sure. Is the POST request being made after the OPTIONS request? or is just an OPTIONS request made only? – danday74 Dec 23 '15 at 16:33
  • 2
    The browser decides whether to make an OPTIONS preflight; Angular cannot change this decision. The OPTIONS request is being sent because the request is a non-simple cross-origin request and requires a preflight. The preflight is failing (because the server doesn't send the right CORS headers) so the actual POST request is never sent. (see http://www.html5rocks.com/en/tutorials/cors/ and/or http://stackoverflow.com/a/10636765/710446) (@user3712353) – apsillers Dec 23 '15 at 16:37
  • This says ... http://stackoverflow.com/questions/22968406/how-to-skip-the-options-preflight-request-in-angularjs ... if you set content type to text plain then NO OPTIONS request will be made - that might help – danday74 Dec 23 '15 at 16:38
  • Thanks apsilleers some nice succinct insight – danday74 Dec 23 '15 at 16:39
0

Not sure if you already have the information you need. But in my local web-server - when I make an http request using postman here is what it adds to the header:

headers: { host: 'localhost', connection: 'keep-alive', pragma: 'no-cache', 'cache-control': 'no-cache', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 Safari/537.36', accept: '/', referer: 'http://localhost/', 'accept-encoding': 'gzip, deflate, sdch', 'accept-language': 'en-US,en;q=0.8' },

And here is what I see in the rawHeaders: [ 'Host', 'localhost', 'Connection', 'keep-alive', 'Pragma', 'no-cache', 'Cache-Control', 'no-cache', 'User-Agent', 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 Safari/537.36', 'Accept', '/', 'Referer', 'http://localhost/', 'Accept-Encoding', 'gzip, deflate, sdch', 'Accept-Language', 'en-US,en;q=0.8' ],

So perhaps you just need to fake your client to be a recognized browser client.

Tim
  • 449
  • 2
  • 7
  • Hey. I don't understand, it's the same headers. – user3712353 Dec 23 '15 at 17:30
  • What I mean is I think you need to put something like this in your code: var headers = { host: 'localhost', connection: 'keep-alive', pragma: 'no-cache', 'cache-control': 'no-cache', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 Safari/537.36', accept: '/', referer: 'http://localhost/', 'accept-encoding': 'gzip, deflate, sdch', 'accept-language': 'en-US,en;q=0.8' }; var options = { host: 'localhost', path: '/', port: '80', headers: headers }; var req = http.request(options, callback); – Tim Dec 23 '15 at 17:43
  • Think I might've just realized the issue - I just noticed your trying to use ssl/tls (thru https). I don't think this will work if you're using angular JS from within a browser client that is pointing at different website. I believe this might be a security restriction: https://docs.angularjs.org/api/ng/service/$http#cross-site-request-forgery-xsrf-protection If you're trying to perform a post to the same site - don't prefix it with the full domain/host: https://xyz/login just use the path: "login/" or "login" – Tim Dec 23 '15 at 17:54