Options method not allowed, CORS to Google Drive - javascript

I've created a script for a google drive which posts to this document.
I invoke it with
var request = $.ajax({
url: "https://script.google.com/macros/s/AKfycbypnRet5l6gUmoGE8oZV2_6da7fImNU12ejHCHCdOambH7UM2CP/exec",
data: serializedData,
type: "POST",
timeout: 10000,
async: true,
crossDomain: true
});
which works from this jsFiddle.
However, when I implement the exact same code into my local project, it doesn't work. I receive
XMLHttpRequest cannot load
https://script.google.com/macros/s/AKfycbypnRet5l6gUmoGE8oZV2_6da7fImNU12ejHCHCdOambH7UM2CP/exec.
No 'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://localhost:3000' is therefore not allowed
access. The response had HTTP status code 405.
405 is "Method not allowed", and when I inspect the network traffic I see that for my local request the method is
Request Method:OPTIONS
However, for the one in jsFiddle it is post as expected. I've done some research, and it seems as if options is a preflight request when doing cross origin, and I can't make it use post directly.
The non-working OPTIONS request has has the following headers that aren't in the working Fiddle's request:
access-control-request-headers:accept, content-type
access-control-request-method:POST
If I post the same request to my server (instead of to Google), I see it contains the request header Content-Type:application/json; charset=UTF-8.
How does jsFiddle get through, when the options method is not allowed? Can I somehow make it skip options and go straight to post?

Preflight OPTIONS requests occur when the request is non-simple, either caused by a non-simple header or a non-simple HTTP method.
The access-control-request-headers: accept, content-type header means that you are attempting to send non-simple headers. Accept is always simple, but Content-Type is only simple when it has the value application/x-www-form-urlencoded, multipart/form-data, or text/plain. It must be the case that your code (for whatever reason) is trying to use a non-simple value for Content-Type, and Google is not providing an Access-Control-Allow-Headers response header to allow it.
Instead, you must specify a simple value for Content-Type. You can do this by adding an explicit contentType: "application/x-www-form-urlencoded; charset=UTF-8" property to your $.ajax options object.

Related

CORS - Response to preflight request doesn't pass access control check

I am trying to send a GET request to a server (the server and the local host have the same domain), however I keep getting the following error:
Response to preflight request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested
resource.
This is the code I use to send the request
$.ajaxSetup({
headers: {
'Access-Control-Allow-Origin': "*",
'Access-Control-Allow-Methods': "GET, POST, PATCH, PUT, DELETE, OPTIONS",
'Access-Control-Allow-Headers': "Origin, Content-Type, X-Auth-Token"
}
});
$.get(myurl, function(data) {
console.log(data);
});
I not sure how to fix this error, since I'm new to web development.
Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
The Access-Control control headers have to be sent by the responding server. Depending on the response and requesting page, browser may allow the request to proceed, out just show the error you got.
If adding the headers was enough, any CORS policy would be moot: any page could access any resource.
the server and the local host have the same domain
I'm not sure what you mean here, but apparently your page is loaded from a different domain than myurl points to.
The Access-Control headers should be sent by the server, not by the client.
You have to set Access-Control in your server app, If your back-end is on java then you can use filter to set Access-Control headers

Response to preflight request doesn't pass access control check Laravel and Ajax call

I have a REST api made in Laravel 5.1 hosted in a remote server. Now, I', trying to consume that API from another website (that I have in local).
In Laravel I set the required lines to send the CORS headers. I also tested the API using Postman and everything seems to be ok!
In the Frontend
Then, in the website I sent the POST request using ajax, with this code:
var url="http://xxx.xxx.xxx.xxx/apiLocation";
var data=$("#my-form").serialize();
$.ajax({
type: "POST",
url: url,
data: data,
headers: { 'token': 'someAPItoken that I need to send'},
success: function(data) {
console.log(data);
},
dataType: "json",
});
Buy then I get this error in the console:
XMLHttpRequest cannot load http://xxx.xxx.xxx.xxx/apiLocation.
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' is therefore not allowed access.
In the Backend
In the API I set this (using a Laravel Middleware to set the headers):
return $next($request)
->header('Access-Control-Allow-Origin', '*')
->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
So, I'm confused about where is exactly the problem.
In the server? but then why with Postman work fine?
Is in the Ajax call? so, then what should I add?
Your backend code must include some explicit handling for OPTIONS requests that sends a 200 response with just the configured headers; for example:
if ($request->getMethod() == "OPTIONS") {
return Response::make('OK', 200, $headers);
}
The server-side code also must send an Access-Control-Allow-Headers response header that includes the name of the token request header your frontend code is sending:
-> header('Access-Control-Allow-Headers', 'token')
but then why with Postman work fine?
Postman isn’t a web app and isn’t bound by same-origin restrictions placed on web apps by browsers to block them from making cross-origin requests. Postman is a browser bolt-on for convenience of testing requests in the same way they could be made outside the browser using curl or whatever from the command line. Postman can freely make cross-origin requests.
https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS in contrast explains how browsers block web apps from making cross-origin requests but also how you can un-block browsers from doing that by configuring your backend to send the right CORS headers.
https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Preflighted_requests explains why the browser is sending that OPTIONS request your backend needs to handle.

302 Response Error

I am making a GET request to my local webservice which I am expecting a 302 response to be returned with a location in the header. However, I get an undefined response back and a network error even though I can see locally that the request is being served and response is being created without any errors in the webservice.
I have tried in Postman and Chrome, and it receives the redirect response and redirects accordingly.
I'm not sure if this is a CORS problem and if so, how can I solve this?
I've already added in the response header for CORS filter
Access-Control-Expose-Headers: Location, [own headers]
Access-Control-Allow-Origin: '*'
Access-Control-Allow-Methods: POST, PUT, GET, OPTIONS, DELETE
Access-Control-Max-Age: [some age]
Access-Control-Allow-Headers: [own headers]
And the location is present in the header when I use Postman
The request I am making using Axios and the config is
const config = {
url: [someURL],
method: 'GET',
headers: {
'customHeader':'token',
},
params: {
[params]
},
maxRedirects: 0,
validateStatus: status => (status >= 200 && status < 300) || status === 302,
};
Any help would really be appreciated as to why the response is undefined when it reaches my JS code, but works fine in Postman and Chrome.
A way I could resolve this is to use HTTP status code 200 and get the location header to redirect, but I want to avoid this because it is technically a redirect response.
The 'customHeader':'token' part of your request triggers your browser to first send a CORS preflight OPTIONS request. Any headers you add to a request other than headers defined as CORS-safelisted request-headers trigger browsers to send a CORS preflight OPTIONS request.
The reason you don’t get this from Postman is that unlike browser engines, Postman doesn’t implement CORS, so it doesn’t send the OPTIONS request. (Postman does not operate under the same-origin Web-security model that browsers enforce for Web applications.)
If the server doesn’t respond in the right way to CORS preflight OPTIONS requests, your request will fail and the only workaround is to not add that 'customHeader':'token' part to your request, or otherwise construct your request in any way that triggers your browser to do CORS preflight.

Access-Control-Allow-Origin:* not being recognized by Angular.js $http

I'm developing a javascript app to control some smart TVs but angular pre lights OPTIONS requests before try the POST request (SOAP) I'm trying to send. The devices return a response with a proper Access-Control-Allow-Origin: * but angular refuses to send the POST request.
Of course, I can't change the configurations of the device's server to send another header angular "needs" and I need to send a Cookie and Content-Type.
How can I work around this?
UPDATE with a screenshot of request (bottom) and response (top) headers.
UPDATE with related angular code:
App is configured with:
app.config(['$httpProvider',function($httpProvider) {
$httpProvider.defaults.withCredentials = true;
}])
The request is:
var body = '<?xml version="1.0"?><s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><s:Body><u:X_SendIRCC xmlns:u="urn:schemas-sony-com:service:IRCC:1"><IRCCCode>{command}</IRCCCode></u:X_SendIRCC></s:Body></s:Envelope>';
var headers = {
"Content-Type": "text/xml; charset=UTF-8",
"SOAPACTION": "urn:schemas-sony-com:service:IRCC:1#X_SendIRCC"
};
return $http({
method:"POST",
url: "http://{ip}/sony/IRCC".replace("{ip}", config.ip),
data: body.replace("{command}", signal),
headers: headers
});
I believe your problem is withCredentials. When you use withCredentials, the server must indicate that allows credentials. In a simple GET request that doesn't require preflighting, the browser is supposed to keep any such response from your app; in a preflighted request, it should not send the actual request.
Here is the best description at mozilla https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials
It says:
but the browser will reject any response that does not have the Access-Control-Allow-Credentials: true header,
and not make the response available to the invoking web content
If you look at the preflight response, you see the headers:
Access-Control-Allow-Headers: "content-type,soapaction"
Access-Control-Allow-Origin: "*"
But the required Access-Control-Allow-Credentials header is not there.
The Options are only fetched if your browser does not know them,
so if you could beforehand load something from the server (regular not via soap) like including an invisible image, your browser should already know the options and not re request them.

CORS issues with jQuery Dropzone and upload to Imgur

I tried to use jQuery Dropzone to upload an image to Imgur or any other domain but that's not working.
This is my dropzone setup:
$("div.dropzone").dropzone
success: -> console.log arguments
paramName: "image"
method: "post"
maxFilesize: 2
url: "https://api.imgur.com/3/upload"
headers:
Authorization: "Client-ID *************"
This doesn't work. It says that return code is 0. The request headers:
Host: api.imgur.com
User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:31.0) Gecko/20100101 Firefox/31.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: fr,fr-fr;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Origin: http://my.opencubes.io
Access-Control-Request-Method: POST
Access-Control-Request-Headers: authorization,cache-control,x-requested-with
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
First as you can see the cient id doesn't appear :(. But the big problem is that the method used is OPTIONS. The response headers:
I have the same problem when I try to upload the file to another domain of mine (the dropzone is located in a subdomain)
In the console I see:
Une demande multi-origines (Cross-Origin Request) a été bloquée : la politique « Same Origin » ne permet pas de consulter la ressource distante située sur https://api.imgur.com/3/upload. Ceci peut être corrigé en déplaçant la ressource sur le même domaine ou en activant CORS.
Which can be translated by
A multi-origin request was blocked: the policy "Same origin" does not allow to see remote resource located in https://api.imgur.com/3/upload. this an be fixed by moving the resource on the samin domain or by enabling CORS.
The OPTIONS request is a normal request: this is used to ask for permissions relative to CORS restrictions. Have a look to this page to understand how CORS work under the hood.
In your case, this is a pure CORS related issue. The OPTIONS request contains this header:
Access-Control-Request-Headers: authorization,cache-control,x-requested-with
Which means: can I use "authorization", "cache-control" and "x-requested-with" headers in my cross-domain AJAX request ?
The response you get is the following:
Access-Control-Allow-Headers :"Authorization, Content-Type, Accept, X-Mashape-Authorization"
Which means: you're allowed to use those headers only: "Authorization", "Content-Type", "Accept", and "X-Mashape-Authorization".
As you can see, "cache-control" and "x-requested-with" are not listed in the allowed list, causing the browser to reject the request.
I've come to 2 test code sample which show this behavior:
Example 1 (working)
var data = new FormData();
data.append('image', 'http://placehold.it/300x500');
var xhr = new XMLHttpRequest();
xhr.open('POST', 'https://api.imgur.com/3/upload', true);
xhr.setRequestHeader('Authorization', 'Client-ID xxxxxxxxxx');
xhr.send(data);
Here are the preflight request's headers sent when running this code (as shown by Firefox 30 devtools, and I've removed unrelated headers such as User-Agent, Accept ...):
OPTIONS https://api.imgur.com/3/upload
Host: api.imgur.com
Origin: http://local.host:8080
Access-Control-Request-Method: POST
Access-Control-Request-Headers: authorization
Cache-Control: no-cache
And the corresponding response's headers
access-control-allow-origin :"*"
Access-Control-Allow-Methods :"GET, PUT, POST, DELETE, OPTIONS"
Access-Control-Allow-Headers :"Authorization, Content-Type, Accept, X-Mashape-Authorization"
Here, we can see that we prompt access to the "authorization" header, and the server is accepting this header, allong with the POST method and any origin URL, so the CORS requirements are satisfied and the request is allowed by the browser.
Example 2 (not working)
var data = new FormData();
data.append('image', 'http://placehold.it/300x500');
var xhr = new XMLHttpRequest();
xhr.open('POST', 'https://api.imgur.com/3/upload', true);
xhr.setRequestHeader('Authorization', 'Client-ID xxxxxxxxxx');
// the only difference with the previous code is this line
xhr.setRequestHeader('Cache-Control', 'no-cache');
xhr.send(data);
Preflight request's headers:
OPTIONS https://api.imgur.com/3/upload
Host: api.imgur.com
Origin: http://local.host:8080
Access-Control-Request-Method: POST
Access-Control-Request-Headers: authorization,cache-control
Cache-Control: no-cache
Preflight response's headers (which is the same as in example 1):
access-control-allow-origin :"*"
Access-Control-Allow-Methods :"GET, PUT, POST, DELETE, OPTIONS"
Access-Control-Allow-Headers :"Authorization, Content-Type, Accept, X-Mashape-Authorization"
Here, the "Access-Control-Request-Headers" header prompt access for "cache-control", which the server does not provide, so the CORS requirements are not satisfied and the request is rejected by the browser.
Here's a JSFiddle referencing different working and not working demos for your problem: http://jsfiddle.net/pomeh/Lfajnebh/. Pay attention to details to understand what's going on, there is few comments but they are here to emphasis trickiest parts of the code.
As a bonus, I've sent a pull request to DropZone's GitHub repository to fix this problem (https://github.com/enyo/dropzone/pull/685) which allows you to remove pref-defined headers by DropZone. Give it a try:
var myDropzone = new Dropzone('.dropzone', {
//...
headers: {
'Authorization': authorizationHeader,
// remove Cache-Control and X-Requested-With
// to be sent along with the request
'Cache-Control': null,
'X-Requested-With': null
}
});
The code above should work with my patched version (https://github.com/pomeh/dropzone/commit/f0063db6e5697888582421865840258dec1ffdc1), whereas the code above should not:
var myDropzone = new Dropzone('.dropzone', {
//...
headers: {
'Authorization': authorizationHeader,
// remove Cache-Control and X-Requested-With
// to be sent along with the request
}
});
You're running into the browser's same-origin security policy. Every browser has one; "origin" basically means "the same site". my JavaScript on example.com can access whatever it likes on example.com, but it's not allowed to read anything from demonstration.com, example.net, or api.example.com. They're from a different origin. Here's a table of what counts as the same origin.
Without it, I could write a web page that steals all your gmail and private Facebook photos. My malicious JavaScript would make web requests to gmail.com and facebook.com, find the links to your emails & photos, load that data too, and then send it off to my own server.
But some services, like APIs, are designed to be accessed by other services. That's where CORS comes in - Cross-Origin Resource Sharing. Web services can use CORS to tell browsers that it's fine to allow access from scripts. If you want to test your code by submitting to your own server, make sure your server is sending the required HTTP response headers.
If you're developing locally, you must also be sure to test from a web server - an address beginning with http://, not file://. The protocol is part of the origin, so you can't submit to an http endpoint from a file URL.
CORS has different types of requests. Some requests are considered simple requests, but others - requests with custom headers - require "preflighting". This means the browser will send a request to the server saying "Is this request OK for CORS?" using the HTTP OPTIONS method before sending the actual request. Any request with custom headers requires preflighting; that's where your HTTP OPTIONS is coming from. jQuery adds a custom X-Requested-With header to AJAX requests, so even if you hadn't added those you'd still see that Options request before the actual POST.
From your screenshots, it looks like Imgur is going to allow your HTTP POST method. Let's move on to figuring out why that's not working.
We're using the Imgur image upload API endpoint. This has one required parameter (image), and if we only want anonymous uploads all we need is a registered app. The upload method lets us send a simple URL to an image for upload, so let's try making an AJAX request to Imgur:
$.ajax
success: (data) -> console.log data
type: "POST"
data:
image: "http://placehold.it/300x500"
url: "https://api.imgur.com/3/upload"
headers:
Authorization: "Client-ID *************" # Don't forget to put your actual Client-ID here!
The next step is to try using the Filereader API to read the file from the form, and send that. Here's a CoffeeScript submit handler for that:
$('#file-form').submit (ev) ->
ev.preventDefault()
file = $('#file-form input[name=file]').get(0).files[0]
$.ajax
success: (data) -> console.log data
type: "POST"
data:
image: file
url: "https://api.imgur.com/3/upload"
headers:
Authorization: "Client-ID *************"
Finally, we can try using Dropzone to achieve the same thing:
$("div.dropzone").dropzone
success: (file, response) ->
console.log file
console.log response
paramName: "image"
method: "post"
maxFilesize: 2
url: "https://api.imgur.com/3/upload"
headers:
"Authorization": "Client-ID *************"
The Dropzone success callback gets two arguments: the file that was uploaded, and the response from the server. You'll probably be most interested in the latter; Imgur sends back an id and a link parameter on success that you can use to show the user their newly-uploaded image.
There's an example project for using the Imgur API from JavaScript available on Github here.

Categories