Django-rest-framework api add SessionAuthentication as optional - python

Hi i'm currently have my api that use this simple-JWT package for jwt token authentication, it worked great. But now when i try to call the api from the django website app using Ajax in which is from a page user already logged in but it still required me to use the jwt access_token.
My ajax call from the page user already logged in:
$.ajax({
type: "POST",
url: "/api/add_favorite/" + property_id + "/",
beforeSend: function (xhr) {
xhr.setRequestHeader('Authorization', 'Bearer {{ refresh_token }}');
},
success: function (data) {
if (data.code == 200) {
alert('added to favorite');
replace_part_1 = '<a id="mylink2" href="#" value="' + property_id +'"><i class="fas fa-heart fa-lg" style="color: red" title="Remove from favorite"></i></a>'
$("a[value='" + property_id + "']").replaceWith(replace_part_1);
}
}
});
Now i don't want to set the header with authorization since in the page user already logged in so the session is already set.
So i tried to add Django Session authentication to the the api like so:
#api_view(['POST'])
#authentication_classes([SessionAuthentication, JWTAuthentication])
#permission_classes([IsAuthenticated])
def add_favorite(request, property_id):
if request.method == 'POST':
try:
favorite_property = Property.objects.get(pk=property_id)
if request.user.is_authenticated:
login_user = request.user
if not login_user.properties.filter(pk=property_id).exists():
login_user.properties.add(favorite_property)
return JsonResponse({'code':'200','data': favorite_property.id}, status=200)
else:
return JsonResponse({'code':'404','errors': "Property already exists in favorite"}, status=404)
except Property.DoesNotExist:
return JsonResponse({'code':'404','errors': "Property not found"}, status=404)
My Ajax after removed the header :
$.ajax({
type: "POST",
url: "/api/add_favorite/" + property_id + "/",
},
success: function (data) {
if (data.code == 200) {
alert('added to favorite');
replace_part_1 = '<a id="mylink2" href="#" value="' + property_id +'"><i class="fas fa-heart fa-lg" style="color: red" title="Remove from favorite"></i></a>'
$("a[value='" + property_id + "']").replaceWith(replace_part_1);
}
}
});
and i removed the set header from the Ajax call now i get 403 return code :
Failed to load resource: the server responded with a status of 403
(Forbidden)
My settings:
REST_FRAMEWORK = {
# Use Django's standard `django.contrib.auth` permissions,
# or allow read-only access for unauthenticated users.
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
],
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_simplejwt.authentication.JWTAuthentication',
'rest_framework.authentication.SessionAuthentication',
),
# 'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',)
}
I don't know why the session authentication doesn't work since the Ajax call is from the page user already logged in.
Thank for reading!

Because you're adding Authentication header on your ajax requests, Django automatically use TokenAuthentication if Authentication exists on the request header. Remove it to use SessionAuthentication.
There might be a problem when you're switch to use SessionAuthentication is that Django will reject your unsafe requests if there is no CSRF token, more detail here

Related

CSRF verification failed. Request aborted in Django rest framework sending the request from flutter

I've followed everything mentioned in both documentation of Django rest-framework and Flutter http but still getting the error ..here is my code :
Django
Settings
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.TokenAuthentication',
]
}
View
#csrf_exempt
#permission_classes(["isAuthenticated"])
#api_view(['POST'])
def chanage_image(request):
data = {}
if request.method == "POST":
token = request.META['HTTP_AUTHORIZATION'][6:]
lang = request.META['HTTP_LANG']
image = request.data['image']
main_user = Token.objects.get(key=token).user
app_user = AppUser.objects.get(main_user=main_user)
format, imgstr = image.split(';base64,')
ext = format.split('/')[-1]
data = ContentFile(base64.b64decode(imgstr), name='temp.' + ext) # You can save this as file instance.
app_user.image = data
app_user.save()
data = {"success": True, "details": AppUserSerializer(
app_user).data, "message": "Image changed" if lang == "en" else "تم تغيير الصورة"}
return Response(data, headers=get_headers())
URLS
path('chanage_image/', chanage_image,name="chanage_image"),
Flutter
Request
Map<String, dynamic> body = {
"image": base64Image,
};
Future<UserModel> changePlayerImage(Map<String, dynamic> body) async {
return await httpClient.post('api/user/change-image',
body: body,
headers: {'referer': 'https://www.l-dawri.com/'}).then((response) {
print(response.body);
return UserModel.fromJson(response.body);
});
}
but still in the end am always getting this error :
CSRF verification failed. Request aborted.
You are seeing this message because this site requires a CSRF cookie when submitting forms.
First you don't sent authorization token into header request while use from drf TokenAuthentication
Also into drf is better you use from class view api(like inheritance from APIView) replace def view's

Python using django framework - You do not have permission to perform this action ERROR

I have my feature on my web app where in an admin account can set verification to a company which is the code of that is below. Now when performing the action i cant seem to figure out what is the reason why it is giving my an error "You do not have permission to perform this action ERROR." - 403 Forbidden . In case you wanted to know the FE i have provided also. Thank you. Hope that helps.
html
<button ng-click="main.verifyCompany(company,true)" ng-show="company.is_verified" class="btn btn-primary btn-sm">
<i class="fas fa-thumbs-up"></i> Approve
</button>
FE (angular)
me.verifyCompany = function (company, verify) {
if (verify) {
var message = "Do you really want to activate job posting and other features for this business account?. Doing so will send an email notification."
} else {
var message = "Deactivate posting for this account?."
}
confirmSweet.ask(message).then(function (response) {
if (response) {
var filter = {}
filter.id = company.id
filter.verify = verify
AdminService.verify_company(me, filter).then(function (response) {
if (response.status == "200") {
console.log("Response", response.status)
me.get_companies()
}
})
}
});
}
PYTHON CODE
class VerifyCompany(APIView):
authentication_classes = (TokenAuthentication,)
permission_classes = (IsAuthenticated,IsCompany,IsAdmin,)
def get(self, request,company_id):
data = request.data
verify = request.GET["verify"]
verify = True if verify == "true" else False
company_ins = Company.objects.get(id=company_id)
company_ins.is_verified = verify
company_ins.save()
data = {}
data["email"] = company_ins.user.email
print("Ang response data")
if verify:
email_ins = Email()
c_task.delay(email_ins.verification_business_success, data)
return Response("Company status has been updated.", status=status.HTTP_200_OK)
Generally When the permissions checks fail either a "403 Forbidden" or "401 Unauthorized" is returned.
Then in this case you should check
Request Was not sucessful,403 will be. return. In this case check Token of the User
Request was successful, but permission was denied.403 will be returned
In this case you should check if user has given list of permissions you have mentioned in the permission classes
more about permissions
Example,
Lets say if user is John
and you have set permissions in View as below
permission_classes = (IsAuthenticated,IsCompany,IsAdmin,)
then john must be user type company and admin

Django + Phonegap Authentication

I am building an application with Phonegap on the client side and a Django server on the backend. I am not able to authenticate the user. Here's my code.
$.ajax({
url: "http://192.168.0.101/userbase/login/",
type: "POST",
dataType: "json",
data: {"username": username,
"account": account,
"password": password,},
success: function (json) {
if (json.logged_in == true) {
window.location.href = "products.html";
}
else {
alert("Invalid Credentials. " + json.error);
}
}
});
This is the AJAX call to log in the user from the index.html. It is authenticated temporarily as in the views.py
# SOME CODE
login(request, user=user)
print(request.user.is_authenticated())
response = JsonResponse(response_data)
response['Access-Control-Allow-Origin'] = '*'
response['Access-Control-Allow-Methods'] = 'OPTIONS,GET,PUT,POST,DELETE'
response['Access-Control-Allow-Headers'] = 'X-Requested-With, Content-Type'
return response
prints True. But, when the window redirects to products.html and I make an AJAX request to my Django server and check if the user is authenticated or not, it returns False. I am not able to find the error.
Please help. Thanks.

"CSRF token missing" with PUT/DELETE meethod rest-framework

I'm using using django rest framework browsable api with ModelViewSet to do CRUD actions and want to use permissions.IsAuthenticatedOrReadOnly, but when I'm logged and try to DELETE or PUT I get
"detail": "CSRF Failed: CSRF token missing or incorrect."
My view looks like this
class objViewSet(viewsets.ModelViewSet):
queryset = obj.objects.all()
serializer_class = objSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
Settings.py
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.AllowAny',
),
Serializer is just
class ObjSerializer(serializers.ModelSerializer):
class Meta:
model = Obj
Although when I delete permission_classes (so the default allowAny triggers) I can it works just fine.
What I want
To be able to PUT/DELETE only when I'm authenticated. I don't know how to send CSRF token, when all happens automatically (modalviewset does the whole work)
In your REST_FRAMEWORK settings you haven't mentioned the Authentication scheme so DRF uses the default authentication scheme which is SessionAuthentication. This scheme makes it mandatory for you to put csrf token with your requests. You can overcome this by:
To make this setting for the whole project in settings.py add
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication',
)}
To make this setting in specific view do the following in your view
class objViewSet(viewsets.ModelViewSet):
queryset = obj.objects.all()
serializer_class = objSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
authentication_classes = (BasicAuthentication,)
source: http://www.django-rest-framework.org/api-guide/authentication/#sessionauthentication
BTW, csrf token is saved as a cookie called 'csrftoken'. You can retrieve it from HTTP Response and attach it to your request header with the key 'X-CSRFToken'. You can see some details of this on: https://docs.djangoproject.com/en/dev/ref/csrf/#ajax
You might have used SessionAuthentication and session auth checks for the csrf_token always and you can avoid the checks by exempting but Lets not do that.
I think you can keep both Authentication classes. or just TokenAuthentication. ie,
'DEFAULT_AUTHENTICATION_CLASSES': (
# 'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.TokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
# 'oauth2_provider.ext.rest_framework.OAuth2Authentication',
# 'rest_framework_social_oauth2.authentication.SocialAuthentication',
),
And If you are not going for TokenAuth and just the session Auth. you can always pass csrf_token via X-CSRFToken header. Or you just can go for csrf_except things. which will avoid the csrf missing issues.
This should work. Also refer the links below.
ref: https://stackoverflow.com/a/26639895/3520792
ref: https://stackoverflow.com/a/30875830/3520792
You shoud add CSRF token to your request. If you do it with JSON request you should add this in your JS code.
It adds CSRF token from user cookies.
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie !== '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
var csrftoken = getCookie('csrftoken');
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
You can remove CSRF check on individual urls. Try this on your urls.py:
url(r'^/my_url_to_view/', csrf_exempt(views.my_view_function), name='my_name'),

How to use TokenAuthentication for API in django-rest-framework

I have a django project, using django-rest-framework to create api.
Want to use token base authentication system so api call for (put, post, delete) will only execute for authorized user.
I installed 'rest_framework.authtoken' and created token for each users.
So, now from django.contrib.auth.backends authenticate, it returns user, with auth_token as attribute. (when loged in successfully).
Now my question is how can I send the token with post request to my api and
at api side how can I verify if token is valid and belongs to the correct user?
Are there any methods in app rest_framework.authtoken to validate given user and its token?
not found this very useful!
Update (changes I made):
Added this in my settings.py:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication',
)
}
Also sending Token in my header but its still not working:
if new_form.is_valid:
payload= {"createNewUser":
{ "users": request.POST["newusers"],
"email": request.POST["newemail"]
}
}
headers = {'content-type' : 'application/json',
'Authorization': 'Token 6b929e47f278068fe6ac8235cda09707a3aa7ba1'}
r = requests.post('http://localhost:8000/api/v1.0/user_list',
data=json.dumps(payload),
headers=headers, verify=False)
"how can I send the token with post request to my api"
From the docs...
For clients to authenticate, the token key should be included in the Authorization HTTP header. The key should be prefixed by the string literal "Token", with whitespace separating the two strings. For example:
Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b
"at api side how can I verify if token is valid and belongs to the correct user?"
You don't need to do anything, just access request.user to return the authenticated user - REST framework will deal with returning a '401 Unauthorized' response to any incorrect authentication.
To answer the first half of your question:
how can I send the token with post request to my api
You can use the Python requests library. For the django-rest-framework TokenAuthentication, the token needs to be passed in the header and prefixed by the string Token (see here):
import requests
mytoken = "4652400bd6c3df8eaa360d26560ab59c81e0a164"
myurl = "http://localhost:8000/api/user_list"
# A get request (json example):
response = requests.get(myurl, headers={'Authorization': 'Token {}'.format(mytoken)})
data = response.json()
# A post request:
data = { < your post data >}
requests.post(myurl, data=data, headers={'Authorization': 'Token {}'.format(mytoken)})
I finally have the django "rest-auth" package working for token authentication.
If this helps, here is the client-side jQuery code that worked for me, after you successfully log in and receive the "auth_token":
var user_url = {API URL}/rest-auth/login
var auth_headers = {
Authorization: 'Token ' + auth_token
}
var user_ajax_obj = {
url : user_url,
dataType : 'json',
headers: auth_headers,
success : function(data) {
console.log('authorized user returned');
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
console.log('error returned from ' + user_url);
}
};
$.ajax(
user_ajax_obj
);
If you are using coreapi. To add the Authorisation you do
import coreapi
auth = coreapi.auth.TokenAuthentication(scheme='Token', token=token_key)
Then you can do
client = coreapi.Client(auth=auth)
response = client.get(my_url)

Categories

Resources