Django Rest Framework permissions outside Rest Framework view - django

I am using Rest Framework Token authentication. Which means I cannot know if a user is authenticated outside a rest framework view eg:(A regular django view). The Rest Framework token authentication is a custom auth system which can only be used in a rest framework view.
In a normal rest framework view, I can restrict the endpoint for authenticated users by using this:
class ExampleView(APIView):
permission_classes = [IsAuthenticated]
def get(self, request, format=None):
content = {
'status': 'request was permitted'
}
return Response(content)
But how will I do that for a regular django view. eg:
def someDjangoView(request):
'''
Note that I cannout use request.user.is_authenticated.
It will always return false as I am using rest framework token authentication.
Which means the request parameter should be of rest framework's and not django's built-in.
'''
content = {"detail": "Only authenticated users should access this"}
return JsonResponse(content)
I am stuck in a situation where I have to know if a user is authenticated (custom auth) outside a rest framework view.
Is there any way to do that?

You can use the api_view decorator to your function-based view to enable DRF:
from rest_framework.decorators import api_view, authentication_classes
#api_view(http_method_names=['GET', 'POST'])
#authentication_classes([YourTokenAuthenticationClass])
def someDjangoView(request):
print(request.user)
...
return JsonResponse(content)

DRF builds on top of the builtin Django contrib.auth user auth system. So, for regular django views, you can use the regular methods provided by contrib.auth.
DRF also supports session-based authentication (usually the default when using contrib.auth). This is ideal, for example, when you have some JavaScript code running in the browser with the user's session.
Note that I cannout use request.user.is_authenticated.
It will always return false as I am using rest framework token authentication
If you are using rest framework token authentication, then you must use views that are compatible with that. request.user.is_authenticated is part of the contrib.auth system built into django. However, you must authenticate a user for this to be True. Rest Framework does this for you. If you're not using the rest framework, you must auth the user yourself!
A simple answer may be to decorate your views to make them utilize the rest framework authentication you define:
#api_view(['GET'])
#authentication_classes(...) # if defaults are not applied
#permission_classes(...) # to apply permissions you need
def view(request):
return Response({"message": "Hello for today! See you tomorrow!"})

Related

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.

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.

Django TokenAuthentication - extending JWT obtain_jwt_token

I have a Django REST app and token authentication powered by Django REST framework JWT Auth
Let me formulate my high-level goal:
My goal is to generate a token for the user if he provides correct credentials AND THEN immediately after successful login I want to perform some additional set of operations. For simplicity, let's say I want to print "Hello" to the console.
Right now my code looks like this:
from rest_framework_jwt.views import obtain_jwt_token
urlpatterns = [
url(r'^api-token-auth/', obtain_jwt_token),
]
What I want to do is the following
Get the token that obtain_jwt_token generates
If authentication was successful, do some additional operations and return the token to the user.
I feel lost in the jungles of all this Django architecture related to authentication classes. Do I understand correctly that if I am using third-party packages like Django JWT, I have no power on login process and there's no way to perform additional operations after the user logs in? And if I want to have more power on login process, I have to do all the job that Django JWT developers have done from scratch? Can I somehow add some operations ON TOP of Django JWT's obtain_jwt_token ?
obtain_jwt_token is just a reference to a view, and as with any view you can just subclass it and reference that instead
class MySpecialJWT(ObtainJSONWebToken):
def post(self, request, *args, **kwargs):
response = super().post(request, *args, **kwargs)
# foo bar
return response

DRF - extend obtain auth token

I have Django Rest Framework with token auth. I have a following url url(r'^api/auth/', views.obtain_auth_token), which returns me token.
What I need to do is perform some db logic, when user performs authorization which is getting the token. I need to query db and do some stuff there.
It seems to me that I have somehow to override default behaviour and add some custom logic to obtain_auth_token.
How can I do that ?
ObtainAuthToken from Rest Framework gets or creates a token for an specific user and then sends it in the Response, all of these behaviour is done in the post method.
The documentation says:
If you need a customized version of the obtain_auth_token view, you can do so by overriding the ObtainAuthToken view class, and using that in your url conf instead.
So you can override the post method, or even create your own APIView to create the Token and add the behaviour you want. In order to do that, change your url:
url(r'^api/auth/', views.custom_obtain_token)
And in views.py:
class CustomObtainToken(APIView):
...
def post(self, request):
<your logic>
<get token n your own way or using DRF way>
return Response({'token': token})
custom_obtain_token = CustomObtainToken.as_view()

How to include user specific access to Django REST API in this example?

My understanding of authentication via an API is that the HTTP request sent by the client needs to include credentials, whether that be just a raw username and password (probably bad practice) or a hashed password, token, etc.
Normally in my Django views, I just use:
request.user.is_authenticated():
If I want my API to be used with an iOS app, this line of code cannot be used because it relies on sessions/cookies?
I would like to edit the following function, to allow it access to a specific user:
api_view(['GET'])
#csrf_exempt
def UserInfoAPI(request):
###if HTTP header includes name and password:###
private_info = Entry.objects.filter(user=request.user)
serializer = EntrySerializer(private_info, many=True)
return Response(serializer.data)
Is there a simple way to manually check for a username/pass in the HTTP header? I don't actually plan to use this in a production environment, but for the sake of understanding, I would like to understand how to have this function verify a username/pass from the http header.
Django REST Framework tries to determine the user that sends the request looking into the Authorization HTTP header. What you should send inside this header depends on the authentication scheme you choose. For example, if you choose BasicAuthentication, your header would be:
Authorization: Basic <"user:password" encoded in base64>
or, if you choose TokenAuthentication:
Authorization: Token <your token>
I would recommend the TokenAuthentication scheme. More schemes are listed in the docs.
To make sure only authenticated users have access to that API's endpoint, use the IsAuthenticated permission. This will check your user's credentials in the request, and if they are not correct, it will raise a HTTP 401 Unauthorized error.
Your Django REST Framework view would look something like this:
from rest_framework import generics
from rest_framework.permissions import IsAuthenticated
class UserInfo(generics.ListAPIView):
model = Entry
serializer_class = EntrySerializer
permission_classes = (IsAuthenticated,)
def get_queryset(self):
queryset = super(UserInfo, self).get_queryset()
user = self.request.user
return queryset.filter(user=user)
As for the code in your iOS app, this post may be helpful.