Making axios request to django - django

I have a complex problem in sending and receiving data in react to django with axios.
I'm not using REST API. This is my Handel function which is related with my signup form tag and after each click on submit button this function executes:
HandelSignUp(e){
e.preventDefault();
let Username = this.refs.username.value;
let Pass = this.refs.pass.value;
let Email =this.refs.email.value;
axios({
url:'http://127.0.0.1:8000/signupAuth/',
mothod:'post',
data:{
username:this.Username,
pass:this.Pass,
email:this.Email
},
headers: {
"X-CSRFToken": window.CSRF_TOKEN,
"content-type": "application/json"
}
}).then(respons =>{
console.log(respons);
})
.catch(err =>{
console.log(err);
});
and also this is my django urls.py :
urlpatterns = [
path('signupAuth/',ReactApp_View.signupRes),
]
ReactApp_View is my views.py in ReactApp file which I imported correctly.
ok now let's see my views.py:
def signupRes(request):
body_unicode = request.body.decode('utf-8')
data = json.loads(myjson)
return HttpResponse(request.body)
after all when I fill my signup fields and then click on button I see this massage in console log of my browser:
Failed to load http://127.0.0.1:8000/signupAuth/: 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:8000' is therefore not allowed access.
What should I do?
and an extra question: what happen in the given url in my axios?

just open your website with the same host url you are trying to call. Use http://127.0.0.1:8000/ instead of localhost

Related

Simulate CSRF attack in Django Rest Framework

I'm trying to get an understanding of how CSRF tokens work, currently my goal is to create a situation where the CSRF attack is possible. I'm hosting two Django apps locally on different ports. I access one by localhost:8000, the other by 127.0.0.1:5000 -- that ensures cookies are not shared between apps.
There's an API view
class ModifyDB(APIView):
def post(self,request,format=None):
if request.user.is_authenticated:
return Response({'db modified'})
else:
return Response({'you need to be authenticated'})
which shouldn't be accessed by unauthenticated users.
The "malicious" site has a button that's supposed to trigger the attack when a user is logged on the target site:
const Modify = () => {
const onClick = async e => { e.preventDefault();
const instance = axios.create({
withCredentials: true,
baseURL: 'http://localhost:8000',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
}
})
const res = await instance.post('/api/modifydb');
return res.data
}
return (
<button class = 'btn' onClick = {onClick}> send request </button>
)
}
My authentication settings are as follows:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'my_proj.settings.CsrfExemptSessionAuthentication',
],
}
where CsrfExemptSessionAuthentication is a custom class that disables csrf protection for my educational purposes:
class CsrfExemptSessionAuthentication(SessionAuthentication):
def enforce_csrf(self, request):
return
django.middleware.csrf.CsrfViewMiddleware is also disabled.
Both CORS_ALLOW_ALL_ORIGINS and CORS_ALLOW_CREDENTIALS are set to true. My question is: what am I doing wrong, because when I'm logged in on the attacked site this axios request from the malicious site returns "you need to be authenticated" response.
I expected that somehow sessionid cookie would be included and request would be successful (which is the logic behind this type of attack if I'm understanding it correctly).
UPDATE
I tried to use a form
<form action="http://localhost:8000/api/modifydb" method="POST">
<input type="submit" value="make a malicious request"/>
</form>
instead of the axios request. It works with this additional setting:
SESSION_COOKIE_SAMESITE = None
while my previous code for request still doesn't work even with this new setting.
Now, I don't get what is the difference between POST requests from a form and from axios regarding cookies attachment. And does SESSION_COOKIE_SAMESITE if enabled prevents this type of attack completely?

How to download a file on client after an Ajax request

I have a django app where users can save their contacts. I am not building a flow to allow users to download their contacts locally.
To do so, I built a CTA ("download") that if clicked
Gets the id of the contact selected
Triggers an Ajax request to my views
In the view I get the contact id, retrieve the data from my DB and create a VCF card out of it. (A contact card - basically a text file)
Now I would like to have such file downloaded on the client's machine but I don't know how to do it.
I managed to do it if I redirect to a new url where the view does exactly what my view below does, I want to allow users to download the file without being redirected to a new page. Also, trying to avoid storing the contact ids in the URL.
That's why I am trying to use ajax but I think that's creating problems because and Ajax request waits JsonReponse from the view.
I tried both a GET or POST but it's not working.
This is my view currently:
def get(self, request, *args, **kwargs):
#Get the ids
ids = request.GET.getlist('contact_ids[]')
# Get contqacts associated with ids
contacts = Contact.objects.filter(id__in=ids)
# Transform contacts into long text
text = Vcard().GroupVcards(contacts)
#Create file on the fly and attach it to the response (is this correct actually?)
response = HttpResponse(content_type='text/plain')
response['Content-Disposition'] = 'attachment;filename=ven.vcf'
response.writelines(text)
#return
return response
This is the Jquery triggering the ajax
$('.Vcard').on('click', function(e) {
let id = $(this).data('contact_id')
$.ajax({
type: "GET",
url: Urls['action:DownloadContact'](),
data: {
csrfmiddlewaretoken: csrftoken,
'contact_ids': [id],
},
error: function(response){
console.log(response)
console.log('error')
},
success: function(response) {
console.log(response)
console.log('success')
}
});
})

Axios PUT Request 403 Forbidden when logged into Django

Whenever I am logged into a django user and I try and send a PUT request to my URL I get a 403 Forbidden Error. However, it works both when I am not logged in and also from the Django Rest API client.
Here is my code in my frontend:
let parameters = `user/${userID}/`
return new Promise((resolve, reject) => {
axios({
method: 'PUT',
url: 'http://127.0.0.1:8000/' + parameters,
data: updatedUser,
headers: {
'Content-Type': 'application/json',
},
})
.then((response) => {
resolve(response)
})
.catch(error => {
console.log(error)
// reject(error)
})
});
I am very confused as I can't see the difference when I am logged into a django user and when I am not, as nothing changes in the frontend. Thanks
EDIT:
This is in my urls.py
path('user/<id>/', views.RetrieveUpdateDestroyUser.as_view()),
And this is the view:
class RetrieveUpdateDestroyUser(RetrieveUpdateDestroyAPIView):
"""
View to handle the retrieving, updating and destroying of a User.
This View will also log any changes made to the model.
"""
serializer_class = UserCreateUpdateSerializer
queryset = CustomUser.objects.all()
lookup_field = 'id'
permission_classes = (AllowAny,)
def update(self, request, *args, **kwargs):
"""
PUT and UPDATE requests handled by this method.
"""
return super().update(request, *args, **kwargs)
I have also tested doing POST and PUT when I am logged into a user and they don't work, but GET does. Thanks
Also tried disabled CSRF but to no avail either
Writing this answer to summarize what we have discovered.
The problem: the AJAX (PUT) call to the DRF endpoint fails with 403 HTTP error for authenticated users and works just fine for anonymous users
Desired Behaviour: make that call working for both anonymous and authenticated users
Reason: by default DRF perform CSRF check for unsafe HTTP methods (POST, PUT, PATCH and DELETE) https://www.django-rest-framework.org/topics/ajax-csrf-cors/
Possible Solutions:
Disable CSRF check like described here https://stackoverflow.com/a/30875830/764182
Pass CSRF token within the PUT request. For more information about CSRF + AJAX in Django read here https://docs.djangoproject.com/en/3.1/ref/csrf/#ajax. For Axios and default Django settings the solution might be:
axios.defaults.xsrfHeaderName = "X-CSRFTOKEN";
axios.defaults.xsrfCookieName = "csrftoken";
axios.defaults.withCredentials = true;

Axios Delete 405 (Method Not Allowed) Error with Django Rest Framework

I am using ReactJS as client side web app and I'm using axios package. In my backend, I am using Django Rest Framework. I created Serializer for CartItem Model:
class CartItemSerializer(serializers.ModelSerializer):
class Meta:
model = CartItem
# Fields you want to be returned or posted
fields = '__all__'
Viewset:
class CartItemViewSet(viewsets.ModelViewSet):
queryset = CartItem.objects.all()
serializer_class = CartItemSerializer
I am trying to use default delete method of DRF in axios using so:
axios.delete('cart_items/', {
headers: { Authorization: 'Token token' },
data: {
id: 1,
},
})
.then(res => {
console.log(res)
})
When I call that, it gives me error in React: DELETE http://127.0.0.1:8000/cart_items/ 405 (Method Not Allowed)
The problem lies in your URL. The URL should point toward the CartItem instance (the URL of DetailView)
So, The URL should be
http://127.0.0.1:8000/cart_items/123/ Where, the 123 is the PK of the instance to be deleted. Also you don't have to attach the payload to the request since it has no effect on DRF side.
axios.delete('cart_items/1/', {
headers: { Authorization: 'Token token' },
})
.then(res => {
console.log(res)
})
The error is in this line
axios.delete('cart_items/',
You have to provide the url of rest endpoint like 'localhost:port/cart_items/'
if the server is running locally on some port

How to properly set up Flask with CORS and Authorization Header

I want to set up CORS within my flask application and adding flask-cors helped up to a certain point. While "CORS(app)" helped with all routes that don't require Authorization, I can't access routes that do.
One route in question looks like this.
#users_blueprint.route('/users/<user_id>', methods=['GET'])
#authenticate
def get_single_user(resp, user_id):
response_object = {
'status': 'failure',
'message': 'Invalid Payload'
}
if resp != user_id:
response_object['message'] = 'You are not authorized to view this user'
return jsonify(response_object), 400
try:
user = User.query.filter_by(user_id=user_id).first()
if user:
response_object['status'] = 'success'
response_object['data'] = user.to_json()
return jsonify(response_object), 200
else:
response_object['message'] = 'The user does not exist'
return jsonify(response_object), 400
except (exc.IntegrityError, ValueError, TypeError) as e:
db.session().rollback()
return jsonify(response_object), 400
I played around with the CORS settings and so far got this:
#enable CORS
CORS(app, allow_headers=["Content-Type", "Authorization", \
"Access-Control-Allow-Credentials"], supports_credentials=True)
But when I try to access the route in question from within my React application via fetch I get this error:
Failed to load
MYROUTE: 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 502. If an opaque response
serves your needs, set the request's mode to 'no-cors' to fetch the
resource with CORS disabled.
What else could I do?
EDIT
When I send a OPTIONS request via CURL I can see that my route in question will respond with "access-control-allow"-headers. So I'm really clueless as to what is going on. Here's also my fetch request:
const url = `myurlendpoint/myuserid`
const token = my_id_token
const options = {
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
};
fetch(url, options)
.then(response => console.log(response.json()))
.catch(error => console.log(error))
ALSO: When I call my route without the header 'Authorization' present I get the correct response saying "No auth provided" and NOT the cross-origin problem. If this route really had not cross-origin-allow set then it should state that in my request without Authorization, right? So it has something to do with the Authorization header...