Getting a csrf error I cant figure out how to fix, i have rest auth working, user is able to update their details like so:
but with Django Comments i get this csrf error using the same csrf token Error:
I would like to get rid of this error on the /comments/post/ endpoint, such that this endpoint behaves similar to /rest-auth/user/ view which accepts an "Authorization: Token 792b5fb27b4fe805e895c91274f26b6ab13cb654" header field to relevant provide data to the authenticated user.
The following is an exert of the csrf related decotaros on the respective views shown in the screen shots:
From the /comments/post/ endpoint
#csrf_protect
#require_POST
def post_comment(request, next=None, using=None):
# Fill out some initial data fields from an authenticated user, if present
data = request.POST.copy()
if request.user.is_authenticated():
if not data.get('name', ''):
data["name"] = request.user.get_full_name() or request.user.get_username()
if not data.get('email', ''):
data["email"] = request.user.email
From the /rest-auth/user/ endpoint
#api_view(['GET'])
#permission_classes((IsAuthenticated, ))
def get_user(request, **kwargs):
pk = request.data['pk']
user = MyUser.objects.get(pk=pk)
serializers = UsersSerializer(user)
return Response(serializers.data)
You're using the wrong content type. Please change it into application/json and try again.
The decorators for your endpoints are different, thus you need to adjust the headers accordingly.
For your /rest-auth/ view the WWW-Authenticate header is required as mentioned here.
The comments view /comments/ endpoint has the csrf_protect decorators which means that the header must match the csrf-token returned in the cookie,as Fede mentions in your header you only require 'X-CSRFToken' with the matching value from the cookie.
I think you are using django-rest-framework which comes with the csfr token exempt by default, but postman is sending a csfr token that is why you are getting that error.
cleaning the cookies might solve the problem.
Related
I have a backend API, it's in django and deployed on Google Endpoint.
I have a post request that insert data to my DB.
I created a script to use this endpoint but I got this error:
{"detail":"CSRF Failed: Referer checking failed - no Referer."}
Regarding over posts I added the crsf_exempt decorator to my class but it did not change.
I try to add the decorator two ways:
class AddUser(APIView):
""" Create user and company from csv """
#method_decorator(csrf_exempt)
def post(self, request):
#method_decorator(csrf_exempt, name='dispatch')
class AddUser(APIView):
""" Create user and company from csv """
def post(self, request):
But both failed.
This is how I contact my endpoint:
resp = requests.request(
method, url,
headers={'Authorization': 'Bearer {}'.format(
open_id_connect_token)}, **kwargs)
Any ideas ?
Thanks
EDIT
So I tried to add authentication classes to my views but it appears to be a bad idea. This is being real trouble for me.
I tried to get the csrftoken doing like this:
client = requests.session()
# Retrieve the CSRF token first
client.get(url) # sets cookie
print(client.cookies)
if 'csrftoken' in client.cookies:
# Django 1.6 and up
csrftoken = client.cookies['csrftoken']
else:
# older versions
csrftoken = client.cookies
Thing is, I am using IAP to protect my API and I do not have any csrftoken cookie but I do have a something looking like this:
<RequestsCookieJar[<Cookie GCP_IAP_XSRF_NONCE_Q0sNuY-M83380ypJogZscg=1
for ...
How can I use this to make post request to my API ?
So this happened to me because I did not set any authentication_classes to my generic view.
When this option is not set Django automatically use the SessionBackend, which need the csrf token.
I fixed it by adding this to my view: authentication_classes = [ModelBackend, GoogleOAuth2]
#Kimor - Can you try doing this in your urls.py
from django.views.decorators.csrf import csrf_exempt
url('^test/$', csrf_exempt(views.TestView.as_view())),
The get and post methods defined on the APIView class just tell DRF how the actual view should behave, but the view method that the Django router expects is not actually instantiated until you call TestView.as_view().
source
Django REST Framework CSRF Failed: CSRF cookie not set
So after working on this project for a while this is what I learned regarding the CSRF using Django.
First of all, if you are using django templates, or in any cases where your back-end and front-end are running behind the same server the most common practice is to use session for authentication.
This is activated by default by DRF.
This means that in your DRF configuration if you do not explicitly set the DEFAULT_AUTHENTICATION_CLASSES option default authentication will be set to Session + BasicAuth.
In this configuration you'll need to manage the CSRF token as described in the documentation (https://docs.djangoproject.com/en/4.0/ref/csrf/).
If your back-end and front-end are separated as in my case, using CSRF is not the only solution or even the recommended one.
As in my case I use JWT behind IAP (Identity Aware Proxy, provided by google). I had to write my own authentication classes and then use it in my DRF conf:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'main.authentication_backend.custom_auth.IAPAuthentication'],
...
}
Here is explain how to write your own authentication class: https://www.django-rest-framework.org/api-guide/authentication/#custom-authentication.
im using flask-praetorian in order to add security to my app.
I got two routes: one for /login and a protected endpoint called /profile. The Login route works fine, it takes the username and password from the form, im able to pass the information to the guard object and authenticate it to get a new token, but im not been able to pass this token to the request headers for the protected endpoint.
I've tried to use the 'session" to add the header, the 'make_response' method, and the redirect(url_for()), but everytime it gets to the endpoint does it without the correct header causing the error.
Code below:
#user.route('/login', methods=['GET', 'POST'])
def login():
form = LoginForm()
if request.method == 'POST':
username = form.username.data
password = form.password.data
user = guard.authenticate(username, password)
token = guard.pack_header_for_user(user)
resp = make_response(profile())
resp.headers['Authorization'] = token['Authorization']
return resp
else:
return render_template('login.html', form=form)
#user.route('/profile')
#auth_required
def profile():
return render_template('profile.html')
(Author of the flask-praetorian package here)
The issue here is that you are just calling the profile() method from your login() method. Calling pack_header_for_user will not actually put the correct auth headers in the current request context. Instead, it just creates a header dict that could be put into a request (but is nicely returned as json). You could jam the token into the current request by tinkering with flask's request context, but it would not be the best way.
The right way to do this is to have your login route include the token in your response payload and then to make another call from your frontend code to the profile endpoint with the token added to the request header.
Really, I think you would be better off going with a different package like flask-security. The flask-praetorian package is intended to be used with pure APIs, and I haven't done any testing or prototyping with standard flask pages.
I'm really stuck at this problem for a couple of days now.
While I understand, what's happening here, I don't really know the best workaround/solution for this.
Problem:
I'm trying to create a user login endpoint using Django and DRF in general.
My login API needs to support a login via password as well as login via OTP.
My LoginView looks like:
def post(self, request, **kwargs):
"""
post
Method to handle user login
:param request:
:param args:
:param kwargs:
:return:
"""
request_data = request.data
login_using_password = request_data.get('login-with-password') is True
login_using_otp = request_data.get('login-with-otp') is True
if request_data is not None:
if all((login_using_password, login_using_otp)):
raise accounts_exceptions.InvalidLoginRequestError()
if login_using_password:
return Response(self._login_with_password(request))
elif login_using_otp:
return Response(self._login_with_otp(request))
raise accounts_exceptions.InvalidLoginRequestError()
return Response(self._login_with_password(request))
Also my _login_with_password looks like:
def _login_with_password(self, request, **kwargs):
"""
_login_with_password
A utility method to handle login with password
:param request:
:return:
"""
return getattr(ObtainJSONWebToken.as_view()(request=request._request, ), 'data')
When I try to login, Django complains saying RawPostDataException You cannot access body after reading from request's data stream
I'm using JWT to authenticate requests. ObtainJSONWebToken is a view provided by DRF-JWT to obtain access tokens to authenticate requests.
What is the workaround/solution for this?
Is there a better way to support such a login requirement?
Thanks in advance!
Resolved this.
There's no concrete way to solve the problem above.
Django disallows access to request.data multiple times.
It could be done only once for the entire request lifetime.
So, this left me with two solutions:
Move my request payload to query params.
Move my request payload to url context.
I ended up using a mix and match of both.
So, basically I used request.query_params and self.context to fetch data from the request and changed my URL and request structure accordingly.
I am building an app with Django 2.1 and I want to be able to do PATCH/DELETE requests through ajax calls. Through researching about this I found out the solution to be to deceive the browser by using a POST request, but setting the header X_METHODOVERRIDE to the desired method.
I would start doing this by creating a middleware that will take care of this. What is the best way of doing?
Please note that I don't want to use Django-REST
Code so far for making the DELETE request:
view.py
class CategoryManageView(StaffRequiredMixin, View):
model = Category
response_dict = {'status': False, 'text': '', 'data': {}}
def delete(self, request, *args, **kwargs):
cat = get_object_or_404(Category, request.POST['id'])
self.response_dict['data'] = cat
cat.delete()
self.response_dict['status'] = True
self.response_dict['text'] = 'Category deleted successfuly'
return JsonResponse(self.response_dict)
If the ajax call method is set to DELETE instead of POST or GET I get error in console:
DELETE http://127.0.0.1:8000/dashboard/admin/categories/manage 403 (Forbidden)
The error code 403 indicates that this is because of CSRF protection. As the CSRF documentation shows, PUT and DELETE - as well as POST - are considered "unsafe" methods, and Django therefore disallows the requests if they don't have a valid CSRF token.
The same page has documentation on how to enable the token in your Ajax requests. Alternatively - although this is strongly discouraged - you can use the #csrf_exempt decorator on the view to disable the protection.
I am using tastypie to create an api for my Django project. My project requires that the user logins in order to make edits to models view etc. So my first step was to login the user using tastypie by creating the login and logout functions in the UserResource as described here. The response contained a sessionid cookie that i used to logout. My resources use SessionAuthentication and DjangoAuthorization. But when i try to post or put or delete on a model using a tastypie resource url, I get an error a 401 error. Some people suggested that i must use a valid csrf_token. But how can i do that when tastypie login view won't return one.
example code:
class EntryResource(ModelResource):
customer = fields.ForeignKey(CustomerResourse, 'customer', full=True)
creator = fields.ForeignKey(UserResource,'creator',null=True,full=True,blank=True)
class Meta:
queryset = Entry.objects.all()
resource_name = 'entry'
authorization = DjangoAuthorization()
authentication = SessionAuthentication()
my client.py
headers={"Content-Type":"application/json"}
#login. I don't get a csrftoken here, but works fine which is odd because i post data r
resp = requests.post(login_url, data=json.dumps(credentials), headers=headers)
cookies ={'sessionid':resp.cookies['sessionid']}
entry = {
'title':'New Entry',
'snippet':'New Snippet',
'created': datetime.datetime.now().strftime('%Y-%m-%dT%H:%M:%S'),
}
entry_json = json.dumps(entry)
entry_add_response = requests.post(entry_api_url,data=entry_json,
headers=headers,cookies=cookies)
#entry_add_response.status_code is 401
#logout works as it should
logout_resp = requests.get(logout_url, cookies=cookies)
what am i doing wrong? Am I not doing the right steps?
all my urls have ?format=json appended to them. Tried without didn't work. I also tried changing from django and session authentication and authorization to basic (Authentication(), Authorization() ). I then get a 500 error. I starting to feel a bit desperate....