16

I'm making a query to a web service using jQuery AJAX. My query looks like this:

var serviceEndpoint = 'http://example.com/object/details?version=1.1';
$.ajax({
  type: 'GET', 
  url: serviceEndpoint,
  dataType: 'jsonp',
  contentType: 'jsonp',
  headers: { 'api-key':'myKey' },
  success: onSuccess,
  error: onFailure
});

When I execute this, I get a status error of 403. I do not understand why my call results in having the status code 403. I'm in control of the security on my service and it is marked as wide-open. I know the key is valid, because I'm using it in another call, which works. Here is the call that works:

var endpoint = 'http://example.com/object/data/item?version=1.1';
$.ajax({ 
  type: 'POST', 
  url: endpoint, 
  cache: 'false',
  contentType:'application/json',
  headers: {
    'api-key':'myKey',
    'Content-Type':'application/json'
  },
  data: JSON.stringify({
    id: 5,
    count:true
  }),
  success: onDataSuccess,
  error: onDataFailure
});

I know these are two different endpoints. But I'm 100% convinced this is not a server-side authentication or permission error. Once again, everything is wide open on the server-side. Which implies that I'm making some mistake on my client-side request.

I feel I should communicate that this request is being made during development. So, I'm running this from http://localhost:3000. For that reason, I immediately assumed it was a CORS issue. But everything looks correct. The fact that my POST request works, but my GET doesn't has me absolutely frustrated. Am I missing something? What could it be?

ykaragol
  • 6,139
  • 3
  • 29
  • 56
Some User
  • 5,257
  • 13
  • 51
  • 93
  • 2
    Did you even try opening that url in your browser directly? Are you missing the `/data/` part of the url to match the one that works? – charlietfl Apr 16 '16 at 15:47
  • 1
    Note that you can't send headers for `jsonp` request, it is a script request. Are you sure you want `jsonp` and not `json`? Also why `JSON.stringify()` for headers? GET has no request `contentType`. since there is no body content being sent. You have numerous issues any one of which can be problem – charlietfl Apr 16 '16 at 15:49
  • @charlietfl I did try opening in the browser. I'm not familiar of any `/data/` part I need to include. I litterally just need to pass in the `version` and `api-key`. I assumed that I should put the `api-key` as a header. Do I need to set the `data` and `contentType` properties to `jsonp`? This seems like it should be a simple call. But clearly, I'm botching it and overlooking something. What should the correct call look like? – Some User Apr 18 '16 at 14:42
  • jsonp is a different type of request than ajax and doesn't allow headers. There simply isn't enough known about problem from what is shown – charlietfl Apr 21 '16 at 01:19
  • are you using chrome? and what is your back end technology? – sangram parmar Apr 21 '16 at 11:45
  • I am using Chrome. All of this is in an index.html file. I'm running the page through lite-server (https://github.com/johnpapa/lite-server) – Some User Apr 21 '16 at 16:24
  • Just a thought, are you using any ad blockers? That has caused issues with me in the past. – applecrusher Apr 22 '16 at 19:15
  • @user1917363 - There are no ad blockers installed. – Some User Apr 23 '16 at 16:34
  • Have you tried the content-type as application/javascript? datatype is jsonp but doesn't seem like content-type should be. – applecrusher Apr 23 '16 at 23:36
  • Have you confirmed that you are setting up valid headers? [ref](http://stackoverflow.com/questions/7686827/how-can-i-add-a-custom-http-header-to-ajax-request-with-js-or-jquery) – hmd Apr 25 '16 at 09:21

3 Answers3

24

The reason of 403 error is you are not sending headers. Since you are making a CORS request, you cannot send any custom headers unless server enables these header by adding Access-Control-Allow-Headers to the response.

In a preflighted-request, client makes 2 requests to the server. First one is preflight (with OPTIONS method) and the second one is the real request. The server sends Access-Control-Allow-Headers header as a response of the preflight request. So it enables some headers to be sent. By this way your POST request can work because the POST request is a preflight-request. But for a GET request, there is no preflight to gather Access-Control-Allow-Headers header and browser doesn't send your custom headers in this case.

A workaround for this issue:

As a workaround, set your dataType and contentType to json as the following:

var serviceEndpoint = 'http://example.com/object/details?version=1.1';
$.ajax({
  type: 'GET', 
  url: serviceEndpoint,
  dataType: 'json',
  contentType: 'json',
  headers: { 'api-key':'myKey' },
  success: onSuccess,
  error: onFailure
});

By this way, your get request will be a preflighted request. If your server enables the api-key with Access-Control-Allow-Headers header, it will work.

Sample server configuration for the above request (written in express.js):

res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', '*');
res.setHeader('Access-Control-Allow-Headers', 'api-key,content-type');
res.setHeader('Access-Control-Allow-Credentials', true);

ADDED:

Actually, contentType should be either application/javascript or application/json while doing a jsonp request. There is no contentType as jsonp.

hygull
  • 8,464
  • 2
  • 43
  • 52
ykaragol
  • 6,139
  • 3
  • 29
  • 56
  • where should the res.setHeader code be written? Also, what kind of object is res? – Zoran777 Oct 16 '17 at 11:22
  • @Zoran777 it will be in the server side. [here is the docs](http://expressjs.com/en/api.html#app.get.method) for `res` in `express` server. – ykaragol Oct 16 '17 at 11:36
1

If you look at the API page for jQuery's Ajax call, it mentions the following in the Content-Type section:

Note: For cross-domain requests, setting the content type to anything other than application/x-www-form-urlencoded, multipart/form-data, or text/plain will trigger the browser to send a preflight OPTIONS request to the server.

That page doesn't really mention what a "preflight OPTIONS request" is, but I found some interesting links when looking that phrase up online:

What's intersting is the code example & the CORS image at the HTML5Rocks page. The image shows how the Ajax calls are being made from the JavaScript code to the browser to the server & how the responses are round-tripping between all 3 of those.

We tend to think of JavaScript + Browser = Client, but in the illustration the author is explaining the difference between the web developer's code & the browser developer's code, where the former is written in JavaScript code, but the latter was written using C, C++ or C# code.

A good packet analyzer tool is Fiddler, which would be similar to Wireshark. Either one of those tools, should show you the pre-flight requests which are being sent from the browser to the server. Most likely, that's where your Ajax request is being blocked at by the server with a 403 Forbidden error.

Clomp
  • 3,168
  • 2
  • 23
  • 36
0

See also this post that discusses the problem of the Google Translate extension appending extraneous HTML to certain page elements.

For example, in the above-linked case, Google Translate appended these lines

<p>&nbsp;</p>
<div id="gtx-trans" style="position: absolute; left: -28px; top: -8px;">&nbsp;</div>

to the TinyMCE editor contents. When the user-entered data was sent to the back-end, it included the Google Translate additions, which precipitated the 403 error.

cssyphus
  • 37,875
  • 18
  • 96
  • 111