I am using django simple_jwt library with DRF for a React project. Everything is working well except I cannot access any data (even simple GET requests) if no access token is provided. I didn't find a way to do it in the docs , is there some kind of decorator, setting to allow simple requests without an access token? Many thanks
If you want to access apis without token , make them public. By default all the api requests are required to be authenticated in your case currently.
Sample:
from rest_framework import permissions
class ExampleApi(APIView):
permission_classes = [permissions.AllowAny]
authentication_classes = []
Related
I'm creating a Login/Register API with Django Rest Framework which is consumed by my frontend, using JWT to authenticate and there are some basic things I can't seem to understand and maybe someone can help me out.
I created an endpoint to register a user (a POST to /users/). At first I was getting a "Authentication credentials were not provided." if I tried sending a request using Postman (on Django API GUI it would work normally I guess because they already send the correct authentication). However, when I think about it, it comes to me that he doesn't have the credentials yet since he's not registered and logged in, so its JWT wasn't created, so I added permission_classes = (AllowAny, ) to my APIView. But then it allows anyone to use the API, therefore anyone would be able to send a PATCH request to update some info without sending the JWT in the request. Anyone have any idea on how to handle that?
I think somehow I'm lacking some kind of concept about authentication. Maybe I need one exclusively for communicating my backend and frontend that will be used to register a user and the users JWT will be used to perform the other actions?
If needed, I can provide other informations about my architecture or code.
First As per your description,
I created an endpoint to register a user (a POST to /users/). At first I was getting a "Authentication credentials were not provided." if I tried sending a request using Postman (on Django API GUI it would work normally I guess because they already send the correct authentication).
You have to understand that since the api is a user registraion api, the permission class should always be set as permission_class = (AllowAny,), but you set permission_class = (IsAuthenticated,) in your view, so django expecting a proper authentication credential(a JWT token as you are using JWT) to make sure the requested user is authenticated. Thats why you are getting a "Authentication credentials were not provided." exception in your POST /users/ api.
Second, as you said later,
However, when I think about it, it comes to me that he doesn't have the credentials yet since he's not registered and logged in, so its JWT wasn't created, so I added permission_classes = (AllowAny, ) to my APIView
its obvious when a user registering himself/herself, then he/she will not have any credentials(JWT token).
then you said,
But then it allows anyone to use the API, therefore anyone would be able to send a PATCH request to update some info without sending the JWT in the request.
From these lines it seems that you are using single api view to Create(POST) and partial update(PATCH) of user. What you have to do is to make separate api views. That is one api view for Create/Register(POST) user and set permission_classes = (AllowAny, ) and another api view to Update(PATCH) user and set permission_class = (IsAuthenticated,). I think this will solve your problem.
EDITION: Now for better understanding how permission works in django rest framework, lets check this the way permission works in django rest framework.
Django has two approaches.
Regular DRF restricts user on Middleware level. So not logged in user doesn't reach anything.
GraphQL, on contrary, uses "per method" approach. So middleware passes all the request and each method. But afterward method calls decorator.
I want to implement 1st approach but for GraphQL. But in that case I need to open path for login mutation. How can I extract mutation name from payload?
If you want restrict a GraphQL API endpoint to Django logged in users, you can do it by extending GraphQLView with LoginRequiredMixin
from django.contrib.auth.mixins import LoginRequiredMixin
from graphene_django.views import GraphQLView
class PrivateGraphQLView(LoginRequiredMixin, GraphQLView):
"""Adds a login requirement to graphQL API access via main endpoint."""
pass
and then adding this view to your urls.py like
path('api/', PrivateGraphQLView.as_view(schema=schema), name='api')
in the usual way as per the docs.
If you don't want to protect your entire API, you can create another schema and endpoint for the unprotected queries and mutations, which allows a clear separation between each. For example in urls.py:
path('public_api/', GraphQLView.as_view(schema=public_schema), name='public_api')
Note that every API endpoint must have at least one query to work or it will cause an assertion error.
Not sure if it serves your purpose, but I've used the following library which used JWT authentication with graphene similar to how JWT with DRF works!
https://github.com/flavors/django-graphql-jwt
Now I made some API with Django rest-framework. And I used JWT for token authentication.
At this moment, I have some questions about token permission.
How can I detect token permission in Django?
For example, There is user A and user B.
When user A wrote article "test", article "test" can only be edited or deleted by user A.
But how can I detect permission about article "test"?
The token will be changed every time when user login. So detect by token is not the correct way.
I searching Google few hours, if there is any solution about this. Is there any way to determine user permission by token?
Thanks in advance!
JWT authentication allows you to identify the user who have previously signed up to your Django app, thanks to the token attached to every request they send to your server.
Then, a permission (that you define and shape as you wish) allows you to let or not let user access / edit a given resource / page.
More practically, what you want to achieve can be set up as follows (assuming you use class based views):
class MyApiEndpoint(ApiView):
authentication_classes = (JSONWebTokenAuthentication,)
permission_classes = (IsOwner,)
where:
JSONWebTokenAuthentication is the authentication class provided by djangorestframework-jwt.
IsOwner is a Permission class you need to write to check if the current user is owner of the resource hosted by the given view / API endpoint. (More on this here).
So I have implemented a token authentication system on my Django web server following the guide from this tutorial:
http://cheng.logdown.com/posts/2015/10/27/how-to-use-django-rest-frameworks-token-based-authentication
but a token is needed in every single api call otherwise I get a unauthorised error. I need to have some api calls free, meaning that anyone, not only a registered user(who will have a token of course) can call it. How is this done?
Looking at the article you have probably set the authentication scheme on a global bases in your settings.
If you only want to have authentication for a few select views, you must not set it globally but per view as explained here:
http://www.django-rest-framework.org/api-guide/authentication/#setting-the-authentication-scheme
class ExampleView(APIView):
authentication_classes = (SessionAuthentication, BasicAuthentication)
permission_classes = (IsAuthenticated,)
Why does Django REST Framework implement a different Authentication mechanism than the built-in Django mechanism?
To wit, there are two settings classes that one can configure:
settings.AUTHENTICATION_BACKENDS which handles the Django-level authentication, and
settings.REST_FRAMEWORK['DEFAULT_AUTHENTICATION_CLASSES'] which authenticates at the REST-Framework level
The problem I'm experiencing is that I have a Middleware layer which checks whether a user is logged-in or not.
When using a web client which authenticates via sessions, this works fine. However, from mobile or when running the test suite (i.e. authenticating using HTTP headers and tokens), the middleware detects the user as an AnonymousUser, but by the time we get to the REST Framework layer, the HTTP Authorization header is read, and the user is logged-in.
Why do these not both happen BEFORE the middleware? Furthermore, why doesn't REST Framework's authentication methods not rely on the Django authentication backend?
Django Rest Framework does not perform authentication in middleware by default for the same reason that Django does not perform authentication in middleware by default: middleware applies to ALL views, and is overkill when you only want to authenticate access to a small portion of your views. Also, having the ability to provide different authentication methods for different API endpoints is a very handy feature.
Rest Framework's authentication methods do not rely on the Django authentication backend because the Django's backend is optimised for the common case, and is intimitely linked to the user model. Rest Framework aims to make it easy to:
Use many different authentication methods. (You want HMAC based authentication? done! This is not possible with django auth framework)
Serve API data without ever needing a database behind it. (You have a redis database with all your data in-memory? Serve it in milliseconds without ever waiting for a round trip to DB user model.)
Thomas' answer explains the why quite well. My answer deals more with what you can do about it. I also have a middleware class that determines the current user. I need this because we have some automatic processes (cron) and I want to attribute these to a super user, and I have some model functions that don't have access to the request object but need to know who the current user is for various functions. So this created some issues as you noticed when we added an API via the REST framework. Here's my solution (yours will need to be adjusted to your use case):
from threading import local
_user = local() # thread-safe storage for current user used by middleware below
class CurrentUserMiddleware(object):
""" Defines Middleware for storing the currently active user """
def process_request(self, request):
if not request:
# Clear the current user if also clearing the request.
_user.value = 1 # One represents automatic actions done by super admin
else:
_user.value = request.user
if not _user.value or not _user.value.is_authenticated:
try:
authenticator = TokenAuthentication()
auth = authenticator.authenticate(request)
if auth:
_user.value = auth[0]
except Exception as e:
pass
def process_response(self, request, response):
_user.value = None
return response
The authenticator bit deals with seeing if the request has been authenticated by the REST Framework and getting the user.