Django rest-api: Postman can't see CSRF token
Asked Answered
P

3

0

I'm using django 1.11.6 and python 3.5 on an ubuntu server.

I have an api for user registration.

This is my curlcommand:

curl -i -H 'Accept: application/json; indent=4'  -X POST  https://mydomain/users/:register/ -d "id=222111&firstname=andy&yearofbirth=2007&lastname=Chris&othernames=" 

When I use it in cygwin I get this response which is the desired:

HTTP/1.1 200 OK
Date: Thu, 26 Oct 2017 06:41:00 GMT
Server: Apache/2.4.18 (Ubuntu)
Allow: POST, OPTIONS
Vary: Accept,Cookie
Content-Length: 188
X-CSRFToken: acY2oPGkkqkzBe9itBq56oFeTFAllqv2bS39c7TpPN9LlGh90E1FxsI0YXLlu1Vu
X-Frame-Options: SAMEORIGIN
Set-Cookie:  csrftoken=QfxnUGTmrRi1MThcn8Qau5ytnt2NR8tdRVCuIY6rWe7dwlp3UbrKV9BfsLdN0JTF; expires=Thu, 25-Oct-2018 06:41:01 GMT; Max-Age=31449600; Path=/
Content-Type: application/json

{
    "isnew": "true",
    "user": {
        "othernames": "",
        "id": "222111",
        "firstname": "Andy",
        "yearofbirth": 2007,
        "lastnames": "Chris"
    }
}

As I can see, I have X-CSRFToken header and `csrftoken' cookie.

When I try to run the same curl command from postman I get:

Forbidden (403)
CSRF verification failed. Request aborted.
You are seeing this message because this HTTPS site requires a 'Referer header' to be sent by your Web browser, but none was sent. This header is required for security reasons, to ensure that your browser is not being hijacked by third parties.
If you have configured your browser to disable 'Referer' headers, please re-enable them, at least for this site, or for HTTPS connections, or for 'same-origin' requests.

My function in views.py is:

class ApiUserRegister(APIView):
    permission_classes = ()
    serializer_class = RegisterUserSerializer

    def post(self, request):
        serializer = RegisterUserSerializer(data=request.data)
        # Check format and unique constraint
        serializer.is_valid(raise_exception=True)
        data = serializer.data

        if User.objects.filter(id=data['id']).exists():
            user = User.objects.get(id=data['id'])
            is_new = "false"
            resp_status = status.HTTP_200_OK
        else:
            user = User.objects.create(id=data['id'],
                                       firstname=data['firstname'],
                                       yearofbirth=data['yearofbirth'],
                                       lastname=data['lastname'],
                                       othernames=data['othernames'])
            user.save()
            is_new = "true"
            resp_status = status.HTTP_201_CREATED
        resp = {"user": serializer.get_serialized(user),
                "isnew": is_new} #csrfmiddlewaretoken: csrf_token
        return Response( resp, status=resp_status, headers = {"X-CSRFToken":get_token(request)})
Pharmacopoeia answered 26/10, 2017 at 6:45 Comment(2)
This is the expected error that you're getting since CSRF verification is enabled. You need to disable the CSRF verification or create CSRF tokens with Django and use those in the requests.Froze
@SachinKukreja I tried yesterday to disable it with no luck. Can you please give me an example of how to create CSRF tokens with Django and use those in the requests? See here my yesterday's question: #46926727Pharmacopoeia
F
3

You can create an additional view to generate CSRF tokens which you can first gather by a GET request.

Example:

# views.py

from django.middleware.csrf import get_token

class CSRFGeneratorView(APIView):
    def get(self, request):
        csrf_token = get_token(request)
        return Response(csrf_token)

# urls.py

urlpatterns += [url(r'generate_csrf/$', views.CSRFGeneratorView.as_view())]

You can then call this view first to gain the CSRF token to use in further requests.

EDIT: After getting the token, you will add this to form data.

Example:

curl -X POST -d "csrfmiddlewaretoken=<token_value>" <url>

Ref: How CSRF works

Froze answered 26/10, 2017 at 7:11 Comment(11)
I tried to use it but I get the message: "detail": "Authentication credentials were not provided."Pharmacopoeia
I disabled 'rest_framework.permissions.IsAuthenticated' and 'rest_framework.authentication.TokenAuthentication' from REST_FRAMEWORK in settings py and now I get it. How to add it to my view?Pharmacopoeia
If you're using this for testing purposes with Postman/cURL, then you can open this view for non-authenticated users, otherwise you have to tell me how you authenticate.Froze
Edited the answer.Froze
Can I get the token value within my view function and not add it manually? I don't have a login mechanism so no authenticated users.Pharmacopoeia
To reach your view function, you have to pass the Csrf Middleware. The middleware stops the execution of the request if the token is not found in the request data.Froze
How do I achieve passing the Csrf Middleware?Pharmacopoeia
Remove SessionAuthentication class from DEFAULT_AUTHENTICATION_CLASSES in settings. This answer will help you further.Froze
OK, thanks. I also have 'rest_framework_jwt.authentication.JSONWebTokenAuthentication' in setings. Do I have to remove that also?Pharmacopoeia
No, it doesnt use the CsrfMiddleware.Froze
I tried your suggested answer but forbidden 403 still remains in postman.Pharmacopoeia
G
0

Good to know

Django sets csrftoken cookie on login. After logging in, we can see the csrf token from cookies in the Postman. (see image) CSRFtoken from cookies


First approach...


We can grab this token and set it in headers manually. But this token has to be manually changed when it expires. This process becomes tedious to do it on an expiration basis.


Better way...


Instead, we can use Postman scripting feature to extract the token from the cookie and set it to an environment variable. In Test section of the postman, add these lines.

var xsrfCookie = postman.getResponseCookie("csrftoken"); postman.setEnvironmentVariable('csrftoken', xsrfCookie.value);

This extracts csrf token and sets it to an environment variable called csrftoken in the current environment. Now in our requests, we can use this variable to set the header.(see image) Set {{csrftoken}} in your header

When the token expires, we just need to log in again and csrf token gets updated automatically.

Thanks to @chillaranand from hackernoon.com for original post

Groan answered 22/4, 2020 at 4:28 Comment(0)
A
0

We have the same issue, we figured that the URL we have some issue.

We have extra slash in the URL we are hitting. Even though in spring boot its getting to correct controller having some extra character character also but Query params are not working

Armenian answered 8/5 at 13:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.