csrf_token verification with Javascript XMLHttpRequest() (403 Forbidden) - javascript

i am trying to send a XMLHttpRequest() post request to the Django Server but it shows 403 Forbidden,
after searching i found that it is due to CSRF verification , after seeing lot of similar content still i am unable to figure out how to implement csrf in XMLHttpRequest
i am including the js snippet that i am using
document.addEventListener("DOMContentLoaded", () => {
document.addEventListener('click',event => {
if (event.target.id === "add-cart-button")
{ event.preventDefault();
const add_cart_button_id = event.target.dataset.pid
const item_class = event.target.dataset.type
const item_name = event.target.dataset.item
const size_chooser = `#${item_class}-size-${add_cart_button_id}`
var sel = document.querySelector(size_chooser)
const size = sel.value
const quantity_chooser = `#${item_class}-quantity-${add_cart_button_id}`
const quantity = document.querySelector(quantity_chooser).value
var request = new XMLHttpRequest()
request.open('POST','/addcart')
request.onload = () => {
const data = request.responseText
}
var data = new FormData()
data.append('iten_class',item_class)
data.append('item_name',item_name)
data.append('size',size)
data.append('quantity',quantity)
request.send(data)
}
})
})
i am sending this request to /addcart route of django server
def addcart(request):
return JsonResponse({'status':True})
which just returns this status
can anyone help me in csrf verification

Related

XMLHttpRequest is requesting the wrong page

I'm trying to create a request that will send a user's information to my server to login. However, every time I try to send the request, it returns information about the page it's currently on /login.php, not the information from the api page /api/login.php.
function login_listener(){
let headers = parse_response_headers(this.getAllResponseHeaders());
if("response" in headers){
let error = document.getElementById("server_response");
error.hidden = false;
error.innerText = headers["response"];
return;
}
//window.location = headers["location"];
}
function login(){
let username = document.getElementById("username").value;
let password = document.getElementById("password").value;
let auth_code = document.getElementById("auth_code").value;
const req = new XMLHttpRequest();
req.addEventListener("load", login_listener);
req.open("POST", "https://{MY_DOMAIN}/api/login.php", true);
req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
req.send(`username=${username}&password=${password}&auth_code=${auth_code}`);
req.onreadystatechange = () => {
console.log(req.getAllResponseHeaders());
}
}
function parse_response_headers(headers){
const arr = headers.trim().split(/[\r\n]+/);
const dct = {};
arr.forEach((line) => {
const parts = line.split(": ");
const header = parts.shift();
dct[header] = parts.join(': ');
});
return dct;
}
The login() function is called when the submit button on the form is clicked. I've checked the network tab in the developer tool pane, and each time I send the request to /api/login.php, it makes a call to /login first, and I can't see the response from the actual api.

Symfony form events not firing when AJAX request converted to vanilla Javascript

I have a simple form that shows/hides different field sets based on the selection of a select menu. This is using the scenario described in the Symfony docs here.
I am in the process of stripping jQuery from this site in favor of vanilla javascript, so I'm attempting to convert the AJAX script that sends the form data after the select changes to either XMLHttpRequest of Fetch API. However when doing so the Form's PreSubmit or PostSubmit events don't fire as they do when using the Ajax request.
At first I thought maybe the request headers were different and in some cases they were, so I made sure on the Fetch API version to exactly match mirror the Ajax request headers, and still the events do not fire. Only the PreSetData event fires.
This is the original AJAX
let $ptoType = $("#pto_type");
$ptoType.off('change').on('change', function () {
let $form = $(this).closest('form');
let data = {};
data[$ptoType.attr('name')] = $ptoType.val();
$.ajax({
url: $form.attr('action'),
type: $form.attr('method'),
data: data,
success: function (html) {
$('#formStep2').replaceWith(
$(html).find('#formStep2')
);
}
});
});
This is the attempt at the XMLHttpRequest
let ptoType = document.getElementById("pto_type");
ptoType.addEventListener('change', function () {
let xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = function() {
if (xmlHttp.readyState === XMLHttpRequest.DONE) {
if (xmlHttp.status === 200) {
document.getElementById("formStep2").innerHTML = xmlHttp.response.getElementById('formStep2').innerHTML;
} else if (xmlHttp.status === 400) {
alert('There was an error 400');
} else {
alert('something else other than 200 was returned');
}
}
};
xmlHttp.open(form.getAttribute('method'), form.getAttribute('action'), true);
xmlHttp.responseType = "document";
xmlHttp.setRequestHeader("X-Requested-With", "XMLHttpRequest")
xmlHttp.send(data);
});
And this is the attempt with the Fetch API
let ptoType = document.getElementById("pto_type");
ptoType.addEventListener('change', function () {
let form = document.getElementById('form_pto_entry');
const formData = new FormData();
formData.append('type', ptoType.options[ptoType.selectedIndex].text);
fetch(form.getAttribute('action'), {
method: form.getAttribute('method'),
headers: new Headers({'content-type': 'application/x-www-form-urlencoded; charset=UTF-8','x-requested-with': 'XMLHttpRequest'}),
body: formData,
})
.then(response => response.text())
.then((body) => {
let bodyHtml = ConvertStringToHTML(body);
document.getElementById("formStep2").innerHTML = bodyHtml.querySelector('#formStep2').innerHTML;
})
.catch((error) => {
console.error('Error:', error);
});
});
let ConvertStringToHTML = function (str) {
let parser = new DOMParser();
let doc = parser.parseFromString(str, 'text/html');
return doc.body;
};
So watching the Network tab in the dev tools and doing a little debugging, the headers sent in the Fetch API version match the headers sent in the Ajax version, however the pre submit data event just never fires.
Based on the doc link you posted, I assumed that you are in symfony 6.1.
You can check the symfony UX component dedicated to this this may fix your problem in no time without any JS configuration. = Symfony UX Dependent form fields doc

How do I res.send object to the backend from the front end?

I am trying to communicate with the frontend to the backend. currently, I have the backend sending an expression to the frontend, and the frontend computes it. from there I want the frontend to send the computed answer back to the backend.
Forexample:
the backend sends "2+2="
the frontend computes that 2+2 = 4
the frontend then sends the answer 4 to the backend
the backend logs the answer 4
Front-end
var XMLHttpRequest = require('xhr2');
const URL = "http://localhost:5000/"
let firstNumber = Math.floor(Math.random() * 10);
let secondNumber = Math.floor(Math.random() * 10);
const express = require('express');
const app = express();
// excecuting random addition
const finalExpression = firstNumber + "+" + secondNumber + "="
console.log(finalExpression);
var xhr = new XMLHttpRequest();
xhr.open("POST", URL, true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JSON.stringify({
expression: finalExpression
}))
Back-end:
const express = require('express')
const app = express()
app.use(express.json())
app.post('/', (req, res) => {
console.log(req.body.expression);
arr = req.body.expression.split("")
console.log(parseInt(arr[0]) + parseInt(arr[2]))
// res.send(parseInt(arr[0]) + parseInt(arr[2]))
})
app.listen(5000, () => console.log())
as you can see, I tried res.send in the frontend to the backend.
You appear to have mixed things up a little.
You can't use express on the front-end - it's a node application. You shouldn't need to use XMLHttpRequest on the server at all. express will handle all the routing for you.
You should use fetch on the front-end to get/post requests to the server (I've used async/await here).
It might look a little more like this.
Server:
// Send an expression to the front-end
app.get('/getExpression', (req, res) => {
res.send(expression);
});
app.post('/postResult', (req, res) {
const result = res.body;
// Calculate whether the result is correct,
// and then send the answer back
res.send(isResultCorrect);
});
Client:
// To get the expression from the server
const response = await fetch('/getExpression');
const expression = await response.text();
// To post the result back to the server
const options = { type: 'POST', body: result };
const response = await fetch('/postResult', options);
const isResultCorrect = await response.text();
You invert frontend and backend :) It is the frontend that sends the XMLHTTPREQUEST and it is the server that processes the request and returns the response.
This being said, using res.send is the right solution to return a response. You did the right thing in BACKEND. Therefore, you can uncomment the // res.send(parseInt(arr[0]) + parseInt(arr[2])) and leave the backend code as it is.
What is missing in the FRONTEND is a code to listen to and handle this response :
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
console.log(xhr.response);
}
}
Add it after var xhr = new XMLHttpRequest();
Your code should then look like this :
var xhr = new XMLHttpRequest();
// New code added here
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
// Handle the response (ex: console.log it)
console.log(xhr.response);
}
}
xhr.open("POST", URL, true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JSON.stringify({
expression: finalExpression
}))

Flask-restful api post gives back empty body via javascript

Having a local database running via python, I'm trying to do some api requests to it via a website. First tried both GET's and POST's as python unittest, which worked fine.
Then using javascript; GET function working perfect but my POST function, whatever I do, sends over an empty body to the python function (variable data in python code) or in other words a dict with nothing in it, while I'm passing data through it.
relevant python snippet:
conn = sq3.connect("temp.db", check_same_thread=False)
class Login(Resource):
def post(self):
data = flask.request.form.to_dict()
lst = conn.execute(f"""SELECT AccountID, Role FROM Account
WHERE Email = \"{data['email']}\"
AND PassW = \"{data['passw_hashed']}\"
""").fetchall()
return {"LoginSucces": [{"AccountId": e[0], "Role": e[1]} for e in lst]}
app = flask.Flask(__name__)
cors = CORS(app, resources={r"/*": {"origins": '*'}})
api = Api(app)
api.add_resource(Login, "/login")
app.run(port=8080)
Javascript:
function req_login(){
let email_p = document.getElementById("login_email").value
let passw = document.getElementById("login_passw").value
let data = JSON.stringify({email: email_p,passw_hashed: passw.hashCode()});
const request = new XMLHttpRequest();
request.open("POST", IP+"/login");
request.setRequestHeader("Accept", "application/json");
request.setRequestHeader('Content-Type', 'application/json');
request.send(data);
request.onload = (e) => {
let jsret = JSON.parse(request.response);
let topkey = Object.keys(jsret);
let data_arr = jsret[topkey];
alert(data_arr['AccountId']);
}
}
Tried with manual data in javascript as well to see if reading out the data was the problem, without succes with the following bit of code:
const data = `{email: "tst#gmail.com", passw: "testtest123"}`;
Where does it go wrong, what can I try/change. If you need any more info, send in comment
After quite a bit of debugging, I found the solution myself. Hope it helps someone:
replace data = flask.request.get_json()
with data = flask.request.json

django sending AJAx POST request using classical javascript into server with csrf_token, how to?

I have this part of code:
document.querySelector('#form_pizza_order').onsubmit = () => {
// make an ajax request to save the pizza order in the server
const request = new XMLHttpRequest();
request.open('POST', '/order_pizza');
// Callback function for when request completes
request.onload = () => {
const data = JSON.parse(request.responseText);
if (data.success) {
// show in cart new order
show_in_cart(data);
}
else {
alert('failed to save pizza order in server');
}
}
const data = new FormData();
let username = localStorage.getItem('username');
data.append('username', username);
//Send request
request.send(data);
return false;
};
that when used the server returns 403 forbidden response because of csrf_token not sent. how do I add the crsf_token header properly with the javascript above, without using jquery. just javascript.
thanks.
function sendData(){
const XHR = new XMLHttpRequest();
// Set up our request
XHR.open("POST", "{% url 'test:index' %}" );
XHR.setRequestHeader('X-Requested-With', 'XMLHttpRequest')
// Bind the FormData object and the form element
let FD = new FormData(form);
// append the token
FD.append('csrfmiddlewaretoken', '{{ csrf_token }}');
// The data sent is what the user provided in the form
XHR.send(FD);
}
let form = document.getElementById('<form_id>')
// take over its submit event.
form.addEventListener("submit", function (event) {
console.log('Submited!')
event.preventDefault();
sendData();
})
In your django views, you can test if the request is ajax:
def index(request):
if request.is_ajax() and request.method='POST':
print(request.POST)
# process post data
Django use X-Requested-With to detect an ajax request, take a look of How django detect ajax request
the sendData function is originated from Mozilla Docs
the following code made it happen:
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie !== '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = cookies[i].trim();
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
function sendData(){
const XHR = new XMLHttpRequest();
// Set up our request
var csrftoken = getCookie('csrftoken');
XHR.open("POST", "/order_pizza" );
XHR.setRequestHeader('X-Requested-With', 'XMLHttpRequest')
XHR.setRequestHeader('X-CSRFToken', csrftoken)
// Bind the FormData object and the form element
let FD = new FormData();
// append the token
FD.append('csrfmiddlewaretoken', csrftoken);
let username = localStorage.getItem('username');
FD.append('username', username);
// The data sent is what the user provided in the form
XHR.send(FD);
}
obviously we had to retrieve the csrf cookie first before we could use it as a form data.

Categories