retro_coder
retro_coder

Reputation: 456

Unable to access API despite entering the token

I have an API that's only accessible by passing a JWT token. I am using Simple-JWT library in Django. Also, I have stored this token within my localStorage and I obtain it within my views module via Ajax. I created an API using the GET method and it should retrieve back certain data from the model. However, when accessing the API URL via the Requests module in Python, it still says that authentication credentials were not provided. See my code below:

models.py:

class Mymodel(models.Model):
    emp= models.ForeignKey(User, on_delete = models.CASCADE)
    entry_time = models.DateTimeField()

views.py:

def get_time(request):
    if (request.method == 'GET'):
        tk = request.GET.get('token') #from AJAX
        headers = {'Authorization': 'JWT {}'.format(tk), 'content-type': "application/json",}
        url = 'http://127.0.0.1:8000/secret_api/'
        response = requests.get(url, headers = headers)
        if response:
            print("success")
            return HttpResponse(response)

class MyAPI(generics.ListAPIView):
    permission_classes = [IsAuthenticated] 
    def get(self, request, format = None):
        user_id = User.objects.get(username=request.GET.get('username')).pk
        user = User.objects.get(id = user_id)
        if not Mymodel.objects.filter(emp=user).exists():
            current_time = timezone.now()
            time = Mymodel.objects.create(emp=user, entry_time=current_time)
            data = time.entry_time
        return data #returns time 

AJAX call:

 $.ajax({
     cache: false,  
     url: {% url 'get_time' %}, #points to the get_time function
     type: 'GET',
     data: {username: username, token: token},
     success: function(data) {
         $("#time").html(data);
     }

However, when executing the get_time function, I am receiving this error:

{"detail": "Authentication credentials were not provided."}

When I change the headers from:

headers = {'Authorization': 'Bearer {}"'.format(tk)}

I am getting this error:

{"detail":"Given token not valid for any token type","code":"token_not_valid","messages":[{"token_class":"AccessToken","token_type":"access","message":"Token is invalid or expired"}]}

I am 100% positive that the token passed from AJAX is correct. Where am I going wrong?

Upvotes: 2

Views: 14192

Answers (2)

bikram
bikram

Reputation: 7945

Did you check your settings.py. Make sure you put correct expiry time of tokens.

In you settings.py

SIMPLE_JWT = {
    'ACCESS_TOKEN_LIFETIME': datetime.timedelta(days=15),
    'REFRESH_TOKEN_LIFETIME': datetime.timedelta(days=15),
}

Make sure you hit python manage.py migrate and restart django server after changing.

Upvotes: 4

Yoomama
Yoomama

Reputation: 143

Solutions: 1. Your token is expired. I recommend you instead access the Simple-JWT models yourself instead of creating your own because you don't have an expiration mechanism. 2. DRF is designed for non-web-browser solution, in my opinion. Change the entire get_time view into a generic viewset.

Quick note: I highly advise you NOT to use JWT (access/refresh) mechanism for website, only for mobile app.


Just an opinion (I recommend you try solution 1, first):

I'm very confused with how you're running this. Django rest framework is designed for non-web-browsers accessing your web server. An AJAX call seems more suitable inside a web page, if I'm correct.

Using JSON web tokens is designed for mobile apps. When you're doing that AJAX call, you're also sending the session ID of the user, so you could just be using request.user. (If anything, you should be accessing the Simple-JWT .token method (not models) instead of storing the tokens yourself since these tokens are also disposed of after expiration.)

So your get_time view should be some kind of generic viewset.


Edit 1: I think your error for MyAPI is that it's supposed to be authentication_classes = [IsAuthenticated], not permission_classes. Make sure you also added simplejwt to your DRF settings.

When I started off learning Django, I was developing a web app that required a huge abstraction from a third party package. I recommend you understand a little bit of Python and a little more of Django so that you can just read these packages' source code and understand them.

For simplejwt, I forgot that tokens are stored in cache for efficiency, not db. If you check the source code here: https://github.com/davesque/django-rest-framework-simplejwt/blob/master/rest_framework_simplejwt/authentication.py, you'll see how authentication works, and then if you go here: https://github.com/davesque/django-rest-framework-simplejwt/blob/master/rest_framework_simplejwt/serializers.py, you'll see how to authenticate access tokens manually (which I highly don't recommend since it's ridiculously complicated if you view the first link).

Generally, you don't have to investigate source code, but it's my recommendation. For example, to understand why I have to use the function retrieve is because of how DRF works.

If you are developing a mobile app or just a simple API endpoint, then you can make a viewset similar to your MyAPI ListAPIView.

views.py

class get_time(mixins.RetrieveModelMixin, viewsets.GenericViewSet):
    authentication_classes = [IsAuthenticated]
    def retrieve(request):  # DRF has default functions that you simply need to override.
        # we know that the user is authenticated already
        current_time = timezone.now()
        if not Mymodel.objects.filter(emp=request.user).exists():
            Mymodel.objects.create(emp=request.user, entry_time=current_time)  # Note, I can't remember if it's request.user or request.user.pk (or request.user.id, same difference there) since I haven't developed with Django in awhile
        return current_time

I don't think I have any issues with your AJAX call, but I don't recommend you using Django-rest-framework (and drf-simple-jwt) because it seems like you're using Django rest framework for general web browser usage instead of just being an API endpoint. You would simply use if request.user.is_authenticated to check if the user is authenticated with a normal Django view. And then remember, it's authentivation_classes. not permission_classes, as noted in the DRF settings that the README in the source code states.

Again, I would only recommend using the API endpoint provided by DRF for mobile app usage, not general purpose web browsing.

Upvotes: 3

Related Questions