CORS issues with jQuery Dropzone and upload to Imgur - javascript

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.

Related

CORS error if PUT has credentials 'include' and Content-Type at the same time

I am using JavaScript fetch() with following options:
window.fetch(path, {
method: 'PUT',
headers: {'Content-Type': 'application/json'},
mode: 'cors',
credentials: 'include',
redirect: 'follow',
referrerPolicy: 'no-referrer',
cache: 'no-cache',
body: JSON.stringify(data)
})
but I get error:
Request header field content-type is not allowed by Access-Control-Allow-Headers in preflight response.
and my Access-Control-Allow-Headers has wildcard *.
It works without Credentials
It works fine if I don't add credentials: include and turn OFF auth on API server.
It works without Content-Type
It works fine if I don't add headers: {'Content-Type': 'application/json'}, and keep auth ON on API server. It means it is sent with text/plain;UTF-8 but content is still JSON.
Headers
Headers from my API server:
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: *
Access-Control-Allow-Origin: http://localhost:3000
Access-Control-Expose-Headers: *
http://localhost:3000 is where is my front-end app running on.
Hints
If i get this error, devTools in Chrome are not able to show me response headers (Access-Control-Allow-Headers etc.) and request headers shows Provisional headers are shown with only content-type: application/json in it and in General there is not method: PUT (there is not method at all). So it looks like Chrome stopped it before firing.
Server is on different domain (intern web) and is running on http with SameSite: None without Secure but I have disabled security Cookies without SameSite must be secure in Chrome to walkaround it in development mode.
Is there any relationship between credentials, content-type and PUT? Is it possible to send PUT with credentials and application/json? If it is not possible - how should I send data with PUT method? FormData which are allowed content-type for cors are not supported by PUT.
See the MDN documentation for Access-Control-Allow-Headers:
The value * only counts as a special wildcard value for requests without credentials (requests without HTTP cookies or HTTP authentication information). In requests with credentials, it is treated as the literal header name * without special semantics. Note that the Authorization header can't be wildcarded and always needs to be listed explicitly.
You need to specify the headers you want to allow explicitly.
Changing Access-Control-Allow-Headers from wildcard * to explicit Content-Type did the trick for me.

Sending cookies in js fetch POST or GET request cross server cors enabled server

Bellow code references and request references have comments these were added in SO to explain my understanding original requests and code contain no comments.
I am aware of the standard of using auth headers for fetch requests. what I need to do is get the cookie that server A sets to server B without having to pass it via javascript.
I have Server A: http://127.0.0.1:8080
contains index.html
index.html when cookie jar is looked at contains a cookie
I also have Server B: http://0.0.0.0:8081. <- duno if relevant port and ip are different
http://127.0.0.1:8080/index.html makes the bellow request
let url = "http://0.0.0.0:8081/write" //this url is o a different server so certain headers are needed
let cookies = document.cookie
console.log(cookies) //this logs the cookie so I know its defo there
let otherPram= {
credentials: 'include', //this is what I need to tell the browser to include cookies
method: "GET"
};
fetch(url, otherPram)
After the request is made the browser makes an options call to http://0.0.0.0:8081/write with response:
access-control-allow-credentials: true
access-control-allow-headers: accept, authorization, content-type, origin, x-requested-with, access-control-allow-credentials, cookie, access-control-allow-origin
access-control-allow-methods: GET, POST, OPTIONS
access-control-allow-origin: http://127.0.0.1. //also tried this with http://127.0.0.1:8080
access-control-expose-headers: Cache-Control, Content-Language, Content-Type, cookie
access-control-max-age: 600
connection: keep-alive
content-length: 0
date: Thu, 16 Jan 2020 08:22:19 GMT
however the request contains no cookies.
to the best of my knowledge it should send the cookies with the fetch request.
Cookies belong to an origin.
let cookies = document.cookie
console.log(cookies) //this logs the cookie so I know its defo there
That shows that there are cookies for the origin of the HTML document.
After the request is made the browser makes an options call to http://0.0.0.0:8081/write
So you are making a cross-origin request.
The cookies do not belong to that origin so the browser will not send them.
If you want to send cookies to http://0.0.0.0:8081/, then you need to make a request to http://0.0.0.0:8081/ and have it use a Set-Cookie header in the response to set them in the first place. The browser won't set the cookies from :8080 to :8081 because they aren't :8081's cookies.
Typically web services will use an Authorization header instead of cookies.
let otherPram= {
headers: {
"Authorization": "Bearer SomeToken"
},
credentials: 'include', //this is what I need to tell the browser to include cookies
method: "GET"
};
Aside: I removed "content-type": "application/json". You are making a GET request so the request has no content to specify the type of.
Experimenting on the server at least with sub domains you can share cookies which is enough in this case.
Along with all the cors properties and the fetch include. The cookie being set needs the domain to be set too.
Set-Cookie: myCookie=value; domain=root.com
All subdomains of root.com will share that specific cookie.

fetch—POST json not working [duplicate]

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.

Options method not allowed, CORS to Google Drive

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.

Cross domain jQuery ajax call with credentials

I've followed the following steps:
Get the server to allow cross domain calls (with all the headers and stuff) This works
Test the server with some cross domain calls This works
Get the server to force a certificate This works
Go to a file on the server with a browser, choose the right certificate and see the file Still works
Now we get to the nice part
Combine the cross domain calls with the certificate <-- this does not work
Problem
I am getting the certificate request from the browser, but when I select the same certificate as I do when using the browser, the call is made but I get a 403 Forbidden.
Code
$.ajax({
type: "POST",
xhrFields: {withCredentials: true},
dataType: "xml",
contentType: "text/xml; charset=\"utf-8\"",
url: "https://www.myOtherServer.com/testfile.asp",
});
Any ideas?
Edit
The Access-Control-Allow-Credentials: true and the Access-Control-Allow-Origin are properly configured.
Additional information
I'm starting to think that it has something to do with the content type. When I change it to "text/html" I get a 415 error, but I do really need to send xml because it is a SOAP server.
Response headers
Access-Control-Allow-Cred... true
Access-Control-Allow-Head... Content-Type, Origin, Man, Messagetype, Soapaction, X-Test-Header
Access-Control-Allow-Meth... GET,POST,HEAD,DELETE,PUT,OPTIONS
Access-Control-Allow-Orig... https://www.mywebsite.com
Access-Control-Max-Age 1800
Cache-Control private
Content-Length 5561
Content-Type text/html; charset=utf-8
Date Wed, 19 Dec 2012 15:06:46 GMT
Server Microsoft-IIS/7.5
X-Powered-By ASP.NET
Request headers
Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding gzip, deflate
Accept-Language nl,en-us;q=0.7,en;q=0.3
Access-Control-Request-He... content-type
Access-Control-Request-Me... POST
Cache-Control no-cache
Connection keep-alive
Host myhoast.com
Origin https://www.mywebsite.com
Pragma no-cache
User-Agent Mozilla/5.0 (Windows NT 6.1; WOW64; rv:17.0) Gecko/20100101 Firefox/17.0
My best guess is that this is a problem not with your Javascript but with your CORS configuration. Did you set up your server with the Access-Control-Allow-Credentials: true header? http://www.w3.org/TR/cors/#access-control-allow-credentials-response-header
Also note that, even when the allow-credentials header is set, the browser will not allow responses to credentialed requests if Access-Control-Allow-Origin is *, according to these docs: https://developer.mozilla.org/en-US/docs/HTTP/Access_control_CORS?redirectlocale=en-US&redirectslug=HTTP_access_control#Requests_with_credentials.
Edit: Since the OP has the CORS headers set up properly, the problem seems to be that the server is rejecting OPTIONS requests with a 403 status code. OPTIONS requests (known as the "preflight request") are sent before certain cross-domain requests (such as POSTs with application/xml content types), to allow the server to notify the browser of what types of requests are allowed. Since the browser doesn't see the 200 response that it expects from the OPTIONS request, it doesn't fire the actual POST request.
basicly we just have to write on htaccess
Header set Access-Control-Allow-Origin “*”
but when we need cookie etc, we had to add script on your ajax code and htaccess
i write about cross domain XHR on my blog, blog.imammubin.com/cross-domain-xhr/2014/05/28/ (Edit: site no longer exists)
hope this help..

Categories