CSRF verification Failed in Firebase and Django integration - python

I am trying to integrate django and DRF with firebase real-time database and while creating a post request I ran into a CSRF verification error.
CSRF verification failed. Request aborted.
Some solutions I found online said that I should use render but I am actually using Django Rest Framework and would like to use something that works with JsonResponse and Serializers. Here's a test code.
def post(self, request):
"""Function to handle post requests
Args:
request (_type_): _description_
"""
# Get the data to be posted from the request
name = request.POST.get('name')
age = request.POST.get('age')
location = request.POST.get('location')
# Set the reference to the database
ref = db.reference('/user1')
# Push the data to the database
ref.push({
'Name': name,
'Age': age,
'Location': location
})
Let me know if I should share how I am creating my firebase client, although I don't think that would be necessary. In conclusion, I would like to find a way to add a csrf_token to a post request method in Django and DRF.
Another solution I saw used csrf_exempt but I don't want to use that either as that is not ideal.
I do plan on taking this to production at some point so do recommend solutions keeping that in mind.

Related

Django session not available on two seperate requests

Description:
In the django session docs it says:
You can read it and write to request.session at any point in your view.
But I can't access the session when making a second request to the same view:
views.py
class Login(APIView):
def post(self, request):
print("before: ", request.session.get("user")
request.session["user"] = "admin"
print(request.session.get("user")) #outputs 'admin'
return Response()
Expected Output:
After the second request (made with jquery $.post) it should output:
"admin"
Output:
Instead it outputs:
None
How can I make sessions available between independend requests?
As mentioned by #AbdulAzizBarkat in the comments, the problem was that the session credentials were not sent to the backend. The way the sessions work in a cross-domain scenario is:
User is verified in backend
Session is sent to the frontend and stored in the browser
The session credentials have to get sent to the backend on every request
You cannot, however, read this session cookies, like mentioned here:
The browser cannot give access to 3rd party cookies like those received from ajax requests for security reasons, however it takes care of those automatically for you!
The provided solution using ajax and setting xhrFields: { withCredentials: true } did not work for me.
Answer:
Instead of an ajax request, I used fetch requests.
It is important to set credentials: "include" since otherwise cookies won't be sent cross-origin. A request looks like this:
fetch(`${API}/login`, {
credentials: "include",
method: "POST",
body: data,
}).then(...).catch(...);

Authenticate an API call correctly with requests and sessions

I want to call my own API in a custom view I wrote. Normally I use JWT authentication with my API calls. In this specific view though, I'd like to use a different authentication.
I want to enable logged in users to make a successful get call (without a token). Not logged in users should not be able to make that call. I tried this with Basic Authentication and Session Authentication but don't really get it tow work.
Here is my view that makes the API call:
def visualize_buildings(request, id):
passed_id = id
endpoint = 'linktomyendpoint' + str(passed_id)
response = requests.get(endpoint)
building_group_data = response.json()
# print(building_group_data)
if 'buildings' in building_group_data:
building_data = building_group_data['buildings']
context = {'building' : building_data}
return render(request, 'building_group_visualize_api.html', context)
else:
return HttpResponseNotFound("Ups. We are sorry but no Building Group was found with that id")
Here my API view:
class BuildingGroupRetrieveAPIView(RetrieveAPIView):
authentication_classes = [JSONWebTokenAuthentication,
SessionAuthentication, BasicAuthentication]
serializer_class = BuildingGroupSerializer
queryset = BuildingGroup.objects.all()
The view works with if I send a token in the headers. But how can I use Session Authentication with that? I tried getting username and password from the request and then pass it to the API call. But that doesn't work because I can't decode the password from the request (which makes sense).
So I tried to follow this: https://2.python-requests.org/en/master/user/advanced/ but I still can't authenticate my request.
Can anyone point me into the right direction? Help is very much appreciated! Thanks in advance!
Session ids are saved as a cookie on the user's device and they will be sent to the server as a header name Cookie. So if you want to use cookies instead of the JWT token then you should send your request with the session id as a cookie header.
This is the header that lets Django know your session-id when you visit the site directly:
Cookie: csrftoken=some-csrf-token; sessionid=your-session-id
Now to make your request contain something like that:
cookies = {'sessionid': 'your-session-id'}
response = requests.get(endpoint, cookies=cookies)
Note that Django might still through an error for csrf token based on your settings.
You can find your session-id on your browser. If you don't know where and how to access them, just google it. it's different based on the browser you use.

Authentication with Django channels using Tokens - How do I use this mixin

My website hosts a lot of APIs(CRUDs) using DJR. I am using DJR Token based authentication and for testing I would add this header to postman
Key : Authorization
value : Token 826fdf3067b07afdf9edd89a6c9facd9920de8b8
and Django Rest Framework would easily be able to authenticate the user.
Now I inlcuded Django channels constantly 1.1.5 and wanted to know how I could do token based authentication. I read this post and it suggests that I copy this mixin to the project. I just started with Django-channels and am not sure how to include that mixin to my code. Currently I have something like this
#channel_session_user_from_http
def ws_connect(message):
user = message.user
message.reply_channel.send({"accept": True}) #Send back Acceptance response.
#channel_session_user
def chat_join(message):
user = message.user #Get authenticated user
I have the following two questions
1-How do I include that mixin in my current project ? I know you include mixins in classes using class classname(SomeMixin). How would I go about including this mixin into my code ?
2-Will I need to include an authentication token in my json message that I send to the websocket ?
Any suggestions would be great.
`

Flask-Security token login without CSRF - Almost there but not quite

I have put in hours in this, and am sure I have a solution that will be useful to many here given the many unanswered questions - if you help me to perfect it
I have a flask Web application that also serves as the backend for my android app.
I wanted to secure my API endpoints with token authentication without globally disabling CSRF Protection.
After all options run out, I decided to modify Flask-security to serve my purpose.
This is my branch. https://github.com/mnjenga/flask-security
In a nutshell, I have created an additional view /api/login that renders a login form whose CSFR is disabled (only that specific form). All other views including /login (in my branch i have amended it to /account/login) retain their CSRF protection. Now when I post to /api/login with the right credentials, I get my token without an error which I can use for any other request. Login API post to /account/login return an error as expected as the view is still protected.
What am now trying to achieve is to make sure the /api/login view is not accessible from a browser or accept any session based authentication (Or is it CSFR safe given it does not return a cookie only a token and I can disable processing of non json posts?)
This is how the /api/login view looks like
#anonymous_user_required
def api_login():
"""View function for api_login view"""
form_class = _security.api_login_form
if request.is_json:
form = form_class(MultiDict(request.get_json()))
else:
form = form_class(request.form)
if form.validate_on_submit():
login_user(form.user, remember=form.remember.data)
after_this_request(_commit)
if not request.is_json:
return redirect(get_post_login_redirect(form.next.data))
if request.is_json:
return _render_json(form, include_auth_token=True)
return _security.render_template(config_value('LOGIN_USER_TEMPLATE'),
login_user_form=form,
**_ctx('login'))
My thoughts currently revolve around making the view process POST method only, replace
else:
form = form_class(request.form)
(return an error here)
so that browser forms are not processed, and remove the form rendering.
I go to sleep now with my head almost bursting, I hope I can wake up to some good news
This is how I have finally done it
#anonymous_user_required
def api_login():
"""View function for login view"""
form_class = _security.api_login_form
if request.is_json:
form = form_class(MultiDict(request.get_json()))
if form.validate_on_submit():
login_user(form.user, remember=form.remember.data)
after_this_request(_commit)
else:
return jsonify(*get_message('INVALID_LOGIN_ATTEMPT'))
if request.is_json:
return _render_json(form, include_auth_token=True)
return jsonify(*get_message('INVALID_LOGIN_ATTEMPT'))
This ensures only json posts are processed, and everything else in the site is CSRF protected.
I can get my token (here i have used ipython)
In [6]: r = requests.post('http://127.0.0.1:5000/api/login', data=json
...: .dumps({'email':'me#mine.com', 'password':'goodpw'}), header
...: s={'content-type': 'application/json'})
In [7]: r.json()
Out[7]:
{'meta': {'code': 200},
'response': {'user': {'authentication_token':
'WyIyIiwiJDUkcm91bmRzPTUzNTAwMCRMUFBteW9sa0p0b0d3eWFBJGFZakdESE9ZeHBrVEJ1YUN4ZC52QVI4VmtPa0x4bEYzSEhwRGM3b1lqdzYi
XQ.DTDLqw.DtFvINasBL6SyT2w2xpyOkWnnIk',
'id': '2'}}}
Attempting the same on my normal login view return an error as expected
In [8]: r = requests.post('http://127.0.0.1:5000/account/login', data=
...: json.dumps({'email':'me.mine.com', 'password':'goodpw'}), he
...: aders={'content-type': 'application/json'})
In [9]: r.json()
Out[9]:
{'meta': {'code': 400},
'response': {'errors': {'csrf_token': ['The CSRF token is missing.']}}}
I am now plan to do the same for registration at some point.
If you wish to test this branch you can install it via pip
pip install git+https://github.com/mnjenga/flask-security

Adding csrf_token to my post request from iPhone app

I have an iPhone application from which I would like to call a post service passing parameters in its request, doing so caused a server error 500.
I have read Django documentation here and I still haven't figure out how to get a csrf_token and how to add it to the AFNetworking AFHTTPRequestOperationManager POST method.
On the server side I've added django.middleware.csrf.CsrfViewMiddleware in the MIDDLEWARE_CLASSES section, but it doesn't seem to do the trick.
My view looks like this; I am not doing much, just hoping to pass.
from django.core.context_processors import csrf
from django.shortcuts import render_to_response
def foo(request):
c={}
c.update(csrf(request))
if request.method == 'POST':
return HttpResponseRedirect("Post received");
The Django CSRF Middleware uses cookies and forms and whatnot to send a code to the page, then make sure the correct page is the one sending information back. In both cases, you must do a GET request to the server, and if you have the middleware installed correctly, it will put the CSRF token into a cookie for you.
Check out the documentation for more info on this.
Now, I noticed you're using a library that uses NSURLConnection, so that should handle cookies for you. I got this bundle of code (untested) that lets you pull the cookie name that you specify in your settings file (again, check out the documentation link above) then put that in your POST.
NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL: networkServerAddress];
for (NSHTTPCookie *cookie in cookies)
{
// get the right cookie
}
Of course, if you're only making POSTs and never GETs first, you don't have a CSRF token to send!
And that's why we have the #csrf_exempt tag. (Docs here) This is the way to go 99% of the time, since most apps you won't do a GET before you do a POST. (in webpages you have to do a GET first). Note that this is intended only when an app is sending only POSTs and there's no session to speak of. You really need to think about your own security when using this, and how you verify that a given app/user really is who they claim to be. And how you disable people from hitting this URL from a webbrowser.
TLDR: Probably use #csrf_exempt on the view, but be careful.

Categories