dojox.form.Uploader 403 error from django server - django

I have a very simple form that I've added the uploader to. When I invoke the uploader, django returns
{"detail":"CSRF Failed: CSRF token missing or incorrect."}
This is the uploader:
var ul = new Uploader(
{
label:"Programmed uploader",
multiple:false,
uploadOnSelect:true,
url:Environment.apiRoot + "upload/",
headers:{
"Accept" : "application/json",
"X-CSRFToken" : dojo.cookie("csrftoken")
}
}).placeAt(form);
I created simple "test" button that invokes a function that performs the same post.
new Button({
name:"Cancel2",
//id:"Cancel",
label:"Cancel" ,
placement:"secondary",
onClick:lang.hitch(this,function(event){
this._testpost()
})
}).placeAt(form);
This is the relavent header from the uploader post
Cookie djdt=hide; csrftoken=WwlARc9OUevblKfgNEDU2Ae4eT9z0kos;sessionid=du37rjyam6v69mw0bgctkbw708xlvc5g
This is the _testpost()
_testpost: function (){
xhr.post({
url: Environment.apiRoot + "upload/",
handleAs: "json",
postData: json.stringify(data),
headers: {
"Content-Type": "application/json",
"Accept" : "application/json",
"X-CSRFToken" : dojo.cookie("csrftoken")
},
loadingMessage: "Submitting form..."
}).then(
lang.hitch(this,function(result) {
form = t._f_form;
dojo.destroy(form);
this._float.destroyRecursive();
alert(result['result_text']);
result['message'] = "Update Request Accepted";
}),lang.hitch(this, function(err){
form = t._f_form;
dojo.destroy(form);
this._float.destroyRecursive();
topic.publish("/application/message","An error occurred.");
}));
this is the relevant header from invoking the _testpost function
Cookie djdt=hide; csrftoken=WwlARc9OUevblKfgNEDU2Ae4eT9z0kos;sessionid=du37rjyam6v69mw0bgctkbw708xlvc5g
X-CSRFToken WwlARc9OUevblKfgNEDU2Ae4eT9z0kos
The key difference being that in the _testpost the X-CSRFToken is put into the header, but on the Uploader post, I don't have any means to put in an X-CSRFToken (my headers attribute seems to just be ignored - i tried it to see if I could get this to work)
Is there any way to get additional headers into the Uploader

Unfortunately, dojox.form.Uploader does not allow headers to be added.
There are a couple options. It sounds like you have access to the csrf token and could append it to the url. Another option may be to provide the csrf token as a cookie and it should be sent with XHR and Flash request.

What I have done (and i'm not sure this is correct), within the django view, disabled csrf checking, and then pull the csrf value out of the header and compare it against the csrf value that is kept in the session record on the server.

you may use dojo.aspect to add the headers to the dojox.form.Uploader.
In case you are using HTML5 upload "plugin", that looks like since you have left the default, you may use something like:
aspect.after(ul, "createXhr", function(xhr) {
xhr.setRequestHeader("Accept", "application/json");
xhr.setRequestHeader("X-CSRFToken", dojo.cookie("csrftoken"));
return xhr;
});
Add this just after you create the Uploader. Also remember to require dojo/aspect.
Notice that this is a bit of a hack and prone to breakage if some change happen in dojox.form.Uploader structure (e.g. they update it to use dojo.promise or other fixes). Also it's implied that this works only for HTML5 plugin, but you may extend the code in the same way to cope for other plugins by inspecting ul.uploadType and make the change specific for that plugin.
This solution works up to and including dojo version 1.12. In 2017 the above announced breakage did happen an this does not work anymore with version of dojo from 1.13 and upward.

Related

Flask cannot parse JSON in Tabulator AJAX request (using advanced configuration)

I regularly use Tabulator's setData() method. I usually set parameters in the URL args, and have no problems with it. But I now have a complex use case that will be easier to solve if I can put a JSON payload into the request.
I've followed the Tabulator documentation for an advanced configuration.
I've made a series of attempts (putting the JSON in various places, using quotes/double quotes in the JSON, etc) at trying to work out the problem. The Flask server always returns this error:
Failed to decode JSON object: Expecting value: line 1 column 1 (char 0)
What makes me suspect the problem is with Tabulator, not Flask, is because I printed request.__dict__ and couldn't find the JSON in the request. (I.e. that seems to the reason for the error.)
The below example, which triggers the same error, is taken from the Fetch documentation (Tabulator uses the Fetch API).
Is there anything wrong with the below or should I be looking harder at Flask?
const data = { username: 'example' };
var ajaxURL = "/data/results";
var ajaxConfig = {
method:"POST",
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': csrf_token,
},
body: JSON.stringify(data)
};
ResultsTable.setData( ajaxURL, {}, ajaxConfig);
Notes:
I'm using the latest version of Tabulator (4.9).
ResultsTable is set elsewhere in the code and is successfully loading default data when the page loads. The use case kicks in when the user sets their own parameters for the data.
The CSRF token, which is set elsewhere in the code, is there because Flask requires it.
The reason that is failing is that Tabulator will build out its own request body when it builds a request and that will override your config.
In your usage case, you will need to override the build in ajax request promise and add your own function that makes the ajax request and then resolves the data.
You can do this using the ajaxRequestFunc.
Checkout the Ajax Request Documentation for full details

Axios Authorization not working - VueJS + Django

I am trying to build an application using VueJS and Django. I am also using Graphene-Django library, as the project utilize GraphQL.
Now, The authentication works fine and i get a JWT Token back.
But when i use the token for other queries that need authentication, i got this error in Vue:
"Error decoding signature"
and the Django Log also returns this:
graphql.error.located_error.GraphQLLocatedError: Error decoding signature
jwt.exceptions.DecodeError: Not enough segments
ValueError: not enough values to unpack (expected 2, got 1)
the bizarre thing is that the same query when executed in Postman just works fine.
As i mentioned in the title is use Axios for my requests, here's an example of a request:
axios({
method: "POST",
headers: { Authorization: "JWT " + localStorage.getItem("token") },
data: {
query: `{
dailyAppoint (today: "${today}") {
id
dateTime
}
}`
}
});
Note: It uses 'JWT' not 'Bearer' because somehow 'Bearer' didn't work for me.
Ok, couple of questions, does you API work without Vue.js from curl. Generate token, check API from curl.
If it does, then check the Headers sent from the request, from Network Inspector, mozilla dev tools/chrome devtools. And update your Post with those RAW Headers.
This particular error arises when your public key is unable to decode the string[token] signed by your private key. Which ultimately means the access token has been tampered with. It could also mean you're sending values like 'unkown'-- JS state initialization error.
Check the RAW headers of the request. It'll help.
Use a request interceptor to set the Authorization header:
axios.interceptors.request.use(config => {
if (localStorage.getItem("token") != null)
config.headers["Authorization"] = "JWT " + localStorage.getItem("token");
return config;
});

CSRF Verification fails in production for Cross Domain POST request

The HTTP_X_CSRFTOKEN header does not match what is inside the csrftoken cookie.
How can I examine the cookie? Set-Cookie is not displayed in the Response header for Cross Domain requests.
I have already followed instructions found in:
CSRF with Django, React+Redux using Axios
Interestingly I found "X-CSRFTOKEN" translates to "HTTP_X_CSRFTOKEN" on the server request header.
Works fine in the development env under localhost (although I am using 2 different ports - one for django and the other my frontend).
UPDATE:
It seems the csrktoken cookie is not correctly set for cross domain rquests (although the browser displays it in the Request Header) so the X-CSRFTOKEN does not get sent.
I ended up adding an API call to return the current csrftoken using a GET request and then sending it back using the X-CSRFTOKEN header.
You haven't mentioned how you're getting the csrftoken from the server in the first place, so I'm assuming it's already present in your browser.
Along with the X-CSRFToken header, also include the cookies in the request using withCredentials: true.
I'm using the js-cookie library to get the csrftoken from the cookies.
import Cookies from 'js-cookie';
axios({
url: 'http://localhost:8000/graphql',
method: 'post',
withCredentials: true,
data: {
query: `
{
// Your query here
}
`
},
headers: {
"X-CSRFToken": Cookies.get('csrftoken')
}
})
Also add CORS_ALLOW_CREDENTIALS = True to your settings.py, assuming you are using django-cors-headers. Otherwise, the cookies won't be accepted.
You will have to make the X-CSRFTOKEN header accessible via the CORS Access-Control-Expose-Headers directive. Example:
Access-Control-Expose-Headers: X-CSRFTOKEN
This header has to be set by your API or web server, so that the browser will see it during the CORS preflight request.

Antiforgery Token Cookie Not Appearing in Request Headers Only in when Embeded in Iframe

I'm trying to embed a simple web app that will POST user input that is running asp.net Core 2.0 into an iframe. The problem I am having is that while embedded, the request headers that are being generated lack the cookie header that contains the .AspNetCore.Antiforgery.[token]. It is being generated as expected outside of the iframe.
This is causing a 400 error because the post is unable to validate the token.
Request Headers generated outside of iframe:
Request Headers: NO IFRAME
Request Headers generated inside of iframe:
Request Headers: INSIDE IFRAME
Has anyone had this issue with the antiforgery token library?
Thanks!!
Turns out the SameSite property on the cookie class for the antiforgery options needs to be set to None for this to work:
services.AddAntiforgery(options =>
{
options.Cookie.SameSite = SameSiteMode.None;
});
The answer from Flyingmartini didn't worked for me, I needed to set these properties as well:
services.AddAntiforgery(options =>
{
options.SuppressXFrameOptionsHeader = true;
options.Cookie.SameSite = SameSiteMode.None;
options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
});

Dart BrowserClient POST not including my cookies

I'm doing a BrowserClient POST across domains and don't see my cookies being included.
This the response I'm getting:
When I send another POST request, I don't see the cookies being included:
Going straight to the test page, I can see the cookies being included:
The Dart code I use to make a POST:
var client = new BrowserClient();
client.post(url, body: request, headers:{"Content-Type" : "application/json", "Access-Control-Allow-Credentials":"true"}).then((res) {
if (res.statusCode == 200) {
var response = JSON.decode(res.body);
callback(response);
} else {
print(res.body);
print(res.reasonPhrase);
}
}).whenComplete(() {
client.close();
});
Not sure about the Access-Control-Allow-Credentials header I'm including, with or without it, nothing changes.
Am I missing headers on the server side that needs to be set on the response or is Dartium blocking cross-domain cookies?
More details on Information Security and the reasoning behind setting cookies via the server.
Update: Enhancement request logged: https://code.google.com/p/dart/issues/detail?id=23088
Update: Enhancement implemented, one should now be able to do var client = new BrowserClient()..withCredentials=true; based on
https://github.com/dart-lang/http/commit/9d76e5e3c08e526b12d545517860c092e089a313
For cookies being sent to CORS requests, you need to set withCredentials = true. The browser client in the http package doesn't support this argument. You can use the HttpRequest from dart:html instead.
See How to use dart-protobuf for an example.