CSRF cookie not set when calling POST request API view - django

I have encountered a weird behavior as it works with one view but does not with another.
Django 3.2 and rest framework 3.13.1 are currently installed within the venv.
When I call the view I get the error message: CSRF validation failed, reason CSRF cookie not set.
This view uses its authentication class that extends TokenAuthentication.
class APIKeyAuthentication(authentication.TokenAuthentication):
logger = logging.getLogger(__name__)
def authenticate(self, request):
# try to get api key via X-API_KEY
api_key = request.META.get("HTTP_X_API_KEY")
# try to get api key via Authorization
if not api_key:
api_key = request.META.get("HTTP_AUTHORIZATION")
if not api_key:
return None
platform_id, api_user = self.validate_api_token(api_key)
return api_user, None
That class is used as default within rest framework:
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": (
"api.authentification.APIKeyAuthentication",
"rest_framework.authentication.BasicAuthentication",
),
"DEFAULT_PERMISSION_CLASSES": [
"rest_framework.permissions.IsAuthenticated",
],
Endpoints are:
path("v1/combinedEntries", VerifyV1OrderView.as_view()),
path("order/verify/", VerifyOrderView.as_view()),
Classes:
class VerifyV1OrderView(GenericAPIView):
serializer_class = CombinedEntryV1Serializer
authentication_classes = (APIKeyAuthentication,)
and
class VerifyOrderView(GenericAPIView):
serializer_class = CombinedEntrySerializer
authentication_classes = (APIKeyAuthentication,)
so I do not understand the error I even removed the session authentication from the config but without any change.
I am using Postman to send the requests but the error occurs also when using curl.
Has someone an idea what the problem could be?
Thanks and regards.
Matt

Related

Django Rest Framework not accepting JWT Authentication Token

I am using the Django Restful API Framework together with Simple JWT and have successfully created a URL for receiving and refreshing a user token.
In order to try out the authentication using the token, I have created a view that simply lists all the posts inside the database. I have then assigned the IsAuthenticated class to the view.
As expected, I get an error message saying that the authentication credentials were not provided. I then went ahead and made a simple GET request using Postman, with the authentication token provided in the "Authorization" tab. The type was set to "Bear Token". Unfortunately, I still get the message "Authentication credentials were not provided." with a 403 Forbidden code.
I have also tried to provide the token in the Headers, as well as make CURL requests, everything to no avail.
My view looks like this:
class PostListView(generics.ListAPIView):
permission_classes = (IsAuthenticated,)
queryset = Post.objects.filter()
This is the serializer:
class PostListSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ('__all__')
The settings.py of the Django project:
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'],
'DEFAULT_PERMISSION_CLASSES': ['rest_framework.permissions.AllowAny'],
'DEFAULT_AUTHENTICATION_CLASSES:': ('rest_framework_simplejwt.authentication.JWTAuthentication',)
}
CORS_ALLOW_ALL_ORIGINS = True # For testing purposes
I have followed several different tutorials online, read through numerous posts as well as followed the official documentation of Simple JWT.
Well what you are doing is trying to filter the data while your basic purpose is to just list your model. For filtering make sure your go through the documentation DRF Filtering.
Try these changes in your code. I hope it will work for you.
Settings.py
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework_simplejwt.authentication.JWTAuthentication',
],
}
Views.py
class UserList(generics.ListAPIView):
permission_classes = (IsAuthenticated,)
queryset = Post.objects.all()
serializer_class = PostListSerializer
After this try to hit your API with access token. To learn more about generic views you can go through this link Generic Views in DRF.

csrf_exempt set but CSRF Failed: Referer checking failed - no Referer

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.

How to test Django REST Framework Login Protected API using postman?

I need to test REST API using postman. API is build using Django REST Framework. Only login user can get access to API. I am not able to find how I can send login credentials using postman. Thanks in advance.
class ApiMemberGroupNameList(views.APIView):
permission_classes = (
permissions.IsAuthenticated,
RequiredOrgPermission,
RequiredOrgStaffMemberPermission)
def get(self, request, **kwargs):
pk = kwargs.get('pk')
hash = kwargs.get('hash')
member_obj = get_object_or_404(Member.objects.filter(org__hash=hash, pk=pk))
return Response(GroupListSerializer(member_obj.groups.all(), many=True).data)
You can use Basic Auth in POSTMAN. Refer to this screenshot:
You could change the HTTP Methods, URLs, Data Payload(under Body tab) etc in the POSTMAN console
UPDATE-1
After your comments, I tried to recreated the problem.What I did was:
created a view
from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
class MySampleView(APIView):
permission_classes = (IsAuthenticated,)
def get(self, request):
return Response(data={"status": True})
Added to urls.py
urlpatterns = [
url(r'^mysampleview/$', MySampleView.as_view())
]
And my POSTMAN response are below:
Authorization Screenshot
Header Screenshot
My conclusion
You may enter wrong credentials, or something else. I would suggest you open a new POSTMAN tab and repeat the procedure, also try to login Django admin using the same credential which is already used in POSTMAN.
If you want to use Basic Authentification to test Django REST API, it must be allowed in Django REST Framework settings:
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.BasicAuthentication',
...

Why is request object not the same between a "native" view and a ModelViewSet?

I am building an API with Django Rest Framework (DRF) and enabled the authentication/registration through social apps.
For authenticating users via their social accounts I use Django rest-framework Social Oauth2 and it works like a charm. To be sure my user is logged in I created a very simple view in the views.py of my app:
def index(request):
return HttpResponse("is_anonymous: %s" % request.user.is_anonymous)
The result in the browser is the following (it means that the user is logged in):
is_anonymous: False
Now as I am building an API with DRF I may need to retrieve some data of the current user (from request.user) in one of my viewsets but in the following code, the result is not what I expected:
class HelloViewSet(viewsets.ModelViewSet):
queryset = Hello.objects.all()
serializer_class = HelloSerializer
# This is just a random ViewSet, what is
# important is the custom view below
#action(detail=False)
def test(self, request):
return Response(request.user.is_anonymous)
Here the result shows that the user not logged in:
True
So the first view shows that request.user.is_anonymous = False and the second shows that request.user.is_anonymous = True. Both views are in the same file views.py in the same app.
What do I miss here? We are not supposed to get the user instance in an API REST?
I suppose this is because your first view is pure Django and it's not using DRF's DEFAULT_AUTHENTICATION_CLASSES. To enable it, you can add #api_view decorator:
from rest_framework.decorators import api_view
from rest_framework.response import Response
#api_view()
def index(request):
return Response("is_anonymous: %s" % request.user.is_anonymous)
Also you should update DEFAULT_AUTHENTICATION_CLASSES to enable OAuth, like this:
REST_FRAMEWORK = {
...
'DEFAULT_AUTHENTICATION_CLASSES': (
...
'oauth2_provider.contrib.rest_framework.OAuth2Authentication',
'rest_framework_social_oauth2.authentication.SocialAuthentication',
),
}
As neverwalkaloner mentioned in the in the comments, the problem was that I didn't pass any access_token in the header via Authorization: Bearer <token>, so of course the server wasn't able to identify the "logged" user that was making the request. Using curl (or Postman) I could add this token for checking purpose and it worked.

How to get a valid csrf token when using tastypie?

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....