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)})
Sachin
  • 3,576
  • 1
  • 15
  • 24
zinon
  • 4,427
  • 14
  • 70
  • 112
  • 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. – Sachin Oct 26 '17 at 06:52
  • @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: https://stackoverflow.com/questions/46926227/run-django-api-from-postman-csrf-verification-failed?noredirect=1#comment80814422_46926227 – zinon Oct 26 '17 at 06:55

2 Answers2

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

Sachin
  • 3,576
  • 1
  • 15
  • 24
  • I tried to use it but I get the message: `"detail": "Authentication credentials were not provided."` – zinon Oct 26 '17 at 07:17
  • 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? – zinon Oct 26 '17 at 07:20
  • 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. – Sachin Oct 26 '17 at 07:20
  • Edited the answer. – Sachin Oct 26 '17 at 07:23
  • 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. – zinon Oct 26 '17 at 07:26
  • 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. – Sachin Oct 26 '17 at 07:28
  • How do I achieve passing the Csrf Middleware? – zinon Oct 26 '17 at 07:32
  • Remove `SessionAuthentication` class from `DEFAULT_AUTHENTICATION_CLASSES` in settings. This [answer](https://stackoverflow.com/questions/30871033/django-rest-framework-remove-csrf) will help you further. – Sachin Oct 26 '17 at 07:33
  • OK, thanks. I also have `'rest_framework_jwt.authentication.JSONWebTokenAuthentication'` in setings. Do I have to remove that also? – zinon Oct 26 '17 at 07:35
  • No, it doesnt use the CsrfMiddleware. – Sachin Oct 26 '17 at 07:36
  • 1
    I tried your suggested answer but `forbidden 403` still remains in `postman`. – zinon Oct 26 '17 at 08:03
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