Run django api from postman: CSRF verification failed - python

I'm trying to run an api using postman. My application is developed in django 1.11.6 using python 3.5.
My app is installed on an ubuntu server. I have no login mechanism to create a csrf token.
These are the steps that I follow:
Click on "import" tab on the upper left side.
Select the Raw Text option and paste my cURL command.
Hit import and I have the command in your Postman builder
Press send button.
My curl command is:
curl -i -H 'Accept: application/json; indent=4' -X POST https://127.0.0.1/users/:register/ -d "id=111&firstname=zinonas&yearofbirth=2007&lastname=Antoniou&othernames="
The error I get is Forbidden (403) - CSRF verification failed. Request aborted.
When I run the curl command via cygwin, it's working properly.
This is the view function that I'm using:
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}
return Response(resp, status=resp_status)
In settings.py I have:
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication',
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
)
}

Try this.
from django.views.decorators.csrf import csrf_exempt
class ApiUserRegister(APIView):
permission_classes = ()
serializer_class = RegisterUserSerializer
#csrf_exempt
def post(self, request):
serializer = RegisterUserSerializer(data=request.data)

To make AJAX requests, you need to include CSRF token in the HTTP header, as described in the Django documentation.
1st option
2nd option

simple just make sure to put as_view()
urlpatterns = [
path('sign_up', views.SignUp.as_view()),
]

update your class to be like this
from braces.views import CsrfExemptMixin
class your_class(CsrfExemptMixin, ......yours_here)
def post(...):
[....]
this will tell django to allow requests without csrf

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
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.
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 to original post

from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.csrf import csrf_protect
#csrf_protect
#csrf_exempt
def home(request):
Add "#csrf_protect, #csrf_exempt" Before the method

In urls file, try this:
urlpatterns = [
url(r'^your_uri/', views.YourView.as_view()),
]
this will tell django to allow requests without csrf

Related

Django Rest Framework - Using Session and Token Auth

I am trying to get this working but don't know if this is possible. It should be doing it like this.
I developed a web app using Django + Rest-Framework + jQuery, and I want to have an external application to consume the same REST API, using JWT Tokens for authentication.
My current config is like this.
settings.py
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.SessionAuthentication',
'rest_framework_simplejwt.authentication.JWTAuthentication',
],
'DEFAULT_RENDERER_CLASS': [
'rest_framework.renderers.JSONRenderer',
]
}
SIMPLE_JWT = {
'AUTH_HEADER_TYPES': ('Bearer',),
}
views.py
class ListFileView(APIView):
permission_classes = (IsAuthenticated,)
def get(self, request, *args, **kwargs):
user = request.user
if user:
obj = Foo.objects.filter(owner=user)
serializer = FooSerializer(obj, many=True)
data = serializer.data
return Response(data, status=status.HTTP_200_OK)
return Response({'detail': 'You have no access to files.'}, status=status.HTTP_400_BAD_REQUEST)
The tricky part is that when I use:
permission_classes = (IsAuthenticated,)
I can perform ajax calls from an external application (using a valid JWT token), but jQuery calls from within the app (with an authenticated user) fail with:
{"detail":"Authentication credentials were not provided."}
And on the other hand, if I use autentication_classes instead of permission_classes:
authentication_classes = (SessionAuthentication, BasicAuthentication)
I can perform ajax calls from within the web app using jQuery, but external calls fail with the same 403 error.
I tried using both like this:
class ListFileView(APIView):
authentication_classes = (SessionAuthentication, BasicAuthentication)
permission_classes = (IsAuthenticated,)
def get(self, request, *args, **kwargs):
...
but the external calls are also rejected.
Is it possible to have these two types of Auth working together in the same class view, or should I separate into two endpoints?
EDIT
Example calls from within the app with jQuery:
<script type="text/javascript">
function loadTblDocs() {
$("#tbl-docs > tbody").empty();
$.ajaxSetup({
headers: { "X-CSRFToken": '{{csrf_token}}' }
});
$.ajax({
type: 'GET',
contentType: "application/json; charset=utf-8",
url: "/api/list/",
dataType: "json",
success: function (data) {
console.log(data);
}
});
};
</script>
And externally via VBA code:
Set web = CreateObject("WinHttp.WinHttpRequest.5.1")
web.Open "GET", "/api/list/", False
web.SetRequestHeader "Authorization", "Bearer <JWT_TOKEN_HERE>"
web.Send
I couldn't work out exactly what is going on in your case, because the behavior in the 3 cases you shared does not seem to be consistent, but going to explain simply how authentication and authorization is determined in DRF, this might help you figure the issue out.
You should be able to use two authentication classes with no problems. With DRF, authentication works like this:
At each request, DRF goes over the provided authentication classes, in the order they are defined. For each class, there are 3 cases:
If it can authenticate the request with the current class, DRF sets request.user. From this point on, this request is authenticated.
If no authentication credentials are present, DRF skips that class
If authentication credentials are present but invalid, such as an invalid JWT token in Authorization header, DRF raises an exception and returns a 403 response.
DRF views normally use the DEFAULT_AUTHENTICATION_CLASSES variable defined in the settings file, but if you provide them in a view, settings are overridden.
Authorization comes into play when you add permission_classes to your views. permission_classes control access to your views, so when you add IsAuthenticated to a view's permission classes, DRF rejects the request with a 403 response if you try to access that view without an authenticated user.
So in your initial case, your internal AJAX request failing in this case suggests that there is no authenticated user data in your request session. I do not know what could be causing this. Perhaps you do not update request session in your login view (Django's login method should do this automatically).
In your second case, you remove permission_classes from the view, so the view will serve the request regardless if there is an authenticated user in the request or not. So it is expected that your internal AJAX request succeeds here, but I do not know why your external request fails in this case.
In your third case, from the point of your internal AJAX request, the scenario seems to be the same as the first case, so I do not know why your internal AJAX request succeeds in this case but not in the first case. The external request failing here is expected because you added the IsAuthenticated permission class to the view, but did not include JWTAuthentication in the authentication_classes, so your request can not be authenticated with your JWT token here.

How to connect Django Rest Framework with contrib.auth views

I am using DRF with JWT (JSON Web Token) authentication. I want to use django's built-in reset password functionality. So, I have included the urls:
url('^', include('django.contrib.auth.urls')),
But, of course, under an API, calling https://.../password_reset/ results on a csrf token missing error. I am wondering which aproach should I take to solve this. Should I change the built in reset_password view and remove csrf protection? Is it a better idea to create a DRF endpoint that accepts the email (reset_password view post parameter) and then somehow generate a csrf token and send it to the view with redirect(reverse("reset_password"), email=email) ... but then, redirect will not send a post request to the reset_password view. Maybe saving the email to session? Any advice will help.
I think in the case of a password reset endpoint it is safe to remove CSRF protection. CSRF protection is for authenticated endpoints to prevent other websites from using a user's stored credentials to gain unauthorized access. Since the PasswordResetForm used by Django doesn't do anything other than send an e-mail, an attacker can't really do much other than annoy a user by spamming them with password reset emails.
You could use a third party for this, but if all you're doing is adding a password reset endpoint, you just need a few lines of code.
views.py
import json
from django.contrib.auth.forms import PasswordResetForm
from django.http.response import HttpResponse
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_http_methods
#csrf_exempt
#require_http_methods(['POST'])
def email_password_reset(request):
# the POST body should be in the format {'email': 'user#user.com'}
try:
# you could also uncomment the following line if you wanted this view to be anonymous only
# assert not request.user.is_authenticated()
assert request.META.get('CONTENT_TYPE', '') == 'application/json'
body = json.loads(request.body)
except (AssertionError, TypeError):
pass
else:
form = PasswordResetForm(body)
if form.is_valid():
form.save()
finally:
return HttpResponse(status=200)
urls.py
urlpatterns = patterns(...
url(r'^/api/password_reset/$', 'email_password_reset', name='email-password-reset')
...)

Django REST Framework Swagger - Authentication Error

I followed the instructions in the docs. So here's my view:
from rest_framework.decorators import api_view, renderer_classes
from rest_framework import response, schemas
from rest_framework_swagger.renderers import OpenAPIRenderer, SwaggerUIRenderer
#api_view()
#renderer_classes([OpenAPIRenderer, SwaggerUIRenderer])
def schema_view(request):
generator = schemas.SchemaGenerator(title='Bookings API')
return response.Response(generator.get_schema(request=request))
And I added the following to my urls.py:
url(r'^docs/', views.schema_view),
When I went to the /docs/ page of my project, I got the following error:
401 : {"detail": "Authentication credentials were not provided."} http://127.0.0.1:8000/docs/?format=openapi
In the browser console I got this message:
Unable to Load SwaggerUI init.js (line 57)
When I set the permission_classes of my schema_view to AllowAny, I was able to view my api docs. However, I'm not sure if this is the right way of doing this. Isn't there a way to login as an admin, or any other user to view the docs. Also, how do I provide the auth tokens when viewing this in the browser? Maybe I missed something in the docs.
I think I've found the solution.
In the settings.py, I added the following settings:
SWAGGER_SETTINGS = {
'SECURITY_DEFINITIONS': {
'api_key': {
'type': 'apiKey',
'in': 'header',
'name': 'Authorization'
}
},
}
Then when I load the page, I just click on the Authorize button at the upper right and enter this value in the value text field:
Token <valid-token-string>
However, I still needed to set the permission class of the schema view to AllowAny. The auth token just let me switch from different users, allowing me to view different set of endpoints.
Isn't there a way to login as an admin, or any other user to view the docs.
If your only use token authentication, first create tokens for your users, then access the resources by setting the header
curl -X GET http://127.0.0.1:8000/api/example/ -H 'Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b'

When tested http POST with chrome POSTMAN, it doesn't work in django

I use Django 1.9.7 & Python 3.5
I implement creating user mechanism and tried to test with POSTMAN(chrome application), but it doesn't work and it shows something like belows:
Forbidden (CSRF cookie not set.): /timeline/user/create/
This is the code :
urls.py
from django.conf.urls import url
From. import views
app_name = 'timeline'
urlpatterns = [
# ex) /
url(r'^$', views.timeline_view, name='timeline_view'),
# ex) /user/create
url(r'^user/(?P<method>create)/$', views.user_view, name='user_view'),
]
views.py
from django.contrib.auth import authenticate, login, logout
from django.shortcuts import render, HttpResponse
from timeline.models import *
def timeline_view(request):
return HttpResponse('hello world')
def user_view(request, method):
if method == 'create' and request.method == 'POST':
print("hi")
username = request.POST.get('username')
username = request.POST.get('username')
user = User.objects.create_user(username, password=password)
user.first_name = request.POST.get('name','')
user.save()
profile = UserProfile()
profile.user = user
profile.save()
return HttpResponse('create success')
else:
return HttpResponse('bad request', status=400)
POSTMAN :
I tried Django CSRF Cookie Not Set but I think this post is for past version.
for testing i used the #csrf_exempt decorator.
from django.views.decorators.csrf import csrf_exempt
#csrf_exempt
def user_view(request, method):
...
now you should be able to call this function without the csrf cookie.
(last time i tried it, i was using django 1.8.7)
source:
https://docs.djangoproject.com/en/1.9/ref/csrf/#edge-cases
You should put CSRFToken in request headers.
After sending request via postman, look at the response Cookies section, take csrftoken value and put in Headers section of request, like this:
key:X-CSRFToken
value: jSdh6c3VAHgLShLEyTjH2N957qCILqmb #your token value
Sometimes Version problem in 'Postman' :
I have face the same problem. While sending the data using the oldest version of postman in POST method.
That time I have received the empty json data in server side.
And I have fix this problem, Once I uninstall the oldest version of postman and installed with latest version.
Use this below statement on top of each and every view function definition (views.py). We don't need to use CRF related statements.
#api_view(["POST", "GET"])
eg:
#api_view(["POST", "GET"])
def GivenInput():
return Response(e.args[0],status.HTTP_400_BAD_REQUEST)
Note*:
But I didn't know that any alternative way to make it global throughout the file.

Django returns 403 error when sending a POST request

when I'm using following Python code to send a POST request to my Django website I'm getting 403: Forbidden error.
url = 'http://www.sub.example.com/'
values = { 'var': 'test' }
try:
data = urllib.urlencode(values, doseq=True)
req = urllib2.Request(url, data)
response = urllib2.urlopen(req)
the_page = response.read()
except:
the_page = sys.exc_info()
raise
When I'm opening any other website it works properly.
example.com is Django website too, and it works properly too.
I think, that's Django config problem, can anyone tell me what should I do to provide access to my script?
Look here https://docs.djangoproject.com/en/dev/ref/csrf/#how-to-use-it.
Try marking your view with #csrf_exempt. That way, Django's CSRF middleware will ignore CSRF protection. You'll also need to use from django.views.decorators.csrf import csrf_exempt. See: https://docs.djangoproject.com/en/dev/ref/csrf/#utilities
Please be advised that by disabling CSRF protection on your view, you are opening a gate for CSRF attacks.
If security is vital to you then consider using #csrf_exempt followed by #requires_csrf_token (see: https://docs.djangoproject.com/en/dev/ref/csrf/#unprotected-view-needs-the-csrf-token). Then, in your script pass this token and that's it.
Does the view that you are posting to have a Django Form on it? If so, I wonder if it's giving a csrf error. I think that manifests itself as a 403. In that case, you'd need to add the {{ csrf_token }} tag. Just a thought.
The response is 403 because django requires a csrf token (included in the post data) in every POST request you make.
There are various ways to do this such as:
Acquiring the token from cookie and the method has been explained in article enter link description here
or
You can access it from DOM using {{ csrf_token }}, available in the template
So now using the second method:
var post_data = {
...
'csrfmiddlewaretoken':"{{ csrf_token }}"
...
}
$.ajax({
url:'url',
type:'POST'
data:post_data,
success:function(data){
console.log(data);
},
error:function(error){
console.log(error);
}
});
Or you can allow the permission to make this post request.
Note: Should be used in the cases where you don't need to authenticate the users for posting anything on our server, say, when a new user registers for the first time.
from rest_framework.permissions import AllowAny
class CreateUser(APIView):
permission_classes = (AllowAny,)
def post(self, request, format=None):
return(Response("hi"))
Further Note that, If you want to make that post request form a different domain (in case when the front of the application is in React or angular and the backend is in Django), make sure the add following in the settings file:
Update the INSTALLED_APPS to use 'coreHeaders' :
INSTALLED_APPS = [
'corsheaders',
]
White list your front end domain by adding following to settings file again:
CORS_ORIGIN_WHITELIST = (
'localhost:8080',
)
Django documentation provides several ways to ensure that CSRF tokens are included. See https://docs.djangoproject.com/en/1.11/ref/csrf/ for details.
I got this error when an authentication Token was expired or when no Token was sent with the request. Using a renewed token fixed the problem.
curl -X POST -H "Authorization: Token mytoken" -d "name=myname&age=0" 127.0.0.1:8000/myapi/
or
curl -X POST -H "Authorization: JWT mytoken" -d "name=myname&age=0" 127.0.0.1:8000/myapi/
depending on Token type.
I too had this problem, because I Tried to access the Main endpoint from another endpoint using '../url' URL Jumping.
My Solution was to add another path for the same viewset;
router.register('main/url',ViewSet,'name');
router.register('secondary/url',ViewSet,'name')
But in Your Case You are Trying to access it from a completely different Location, From Django's Point of view So You need to mark you ViewSet with #crsf_exempt middleware which will Disable Security Protocols Related to CRSF.

Categories