1

I have a problem with JWT authentication using django-rest-knox.

Error is: Detail: Authentication credentials were not provided.

ENDPOINT: /api/auth/login/

Headers in the POST request to the endpoint: { Content-Type: application/json }

body:

{
    "username": "admin",
    "password": 1234
}

Login API View:

class UserLoginAPIView(generics.GenericAPIView):
    serializer_class = UserLoginSerializer

    def post(self, request, *args, **kwargs):
        data = request.data
        serializer = self.get_serializer(data=data)
        serializer.is_valid(raise_exception=True)
        user = serializer.validated_data
        token = AuthToken.objects.create(user)
        return Response({
            "user": UserSerializer(user,
                                   context=self.get_serializer_context()).data,
            "token": token
        })

Serializer:

class UserLoginSerializer(serializers.Serializer):
    username = serializers.CharField()
    password = serializers.CharField()


    def validate(self, data):
        user = authenticate(**data)
        if user and user.is_active:
            return user
        raise serializers.ValidationError("Invalid Credentials")

Default Settings:

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'knox.auth.TokenAuthentication',
    ]
}
1

4 Answers 4

1

I think your procedure is wrong. According to Knox documents, You need to give an access permission login endpoint. But you did not give permission to access your login endpoint. So your login endpoint looks like this,

# views.py 
from django.contrib.auth import login
from rest_framework import permissions
from rest_framework.authtoken.serializers import AuthTokenSerializer
from knox.views import LoginView as KnoxLoginView

class LoginView(KnoxLoginView):
    permission_classes = (permissions.AllowAny,)

    def post(self, request, format=None):
        serializer = AuthTokenSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        user = serializer.validated_data['user']
        login(request, user)
        return super(LoginView, self).post(request, format=None)

# settings.py 
REST_KNOX = {
  'USER_SERIALIZER': 'knox.serializers.UserSerializer',
}

If you use the user serializer in your setting you get token with the username of the requesting user like bellow

{"user":{"username":"admin"},"token":"00bd2a5e517800b75a8f36bbf3baea4c839169108b25a5a5ea599a4ecda974c0"}

More details here. Knox

Sign up to request clarification or add additional context in comments.

Comments

0

Hi it seems that you are using knox which is not exactly the same, but in essence it is similar. Your view seems protected probably because of your DEFAULT_PERMISSION_CLASSES settings.

If you to be able to login via this view you'd have to set

permission_classes = [AllowAny, ]

on your view.

However I would not do that since this is a hack. (I know I have been through it and we red the same article).

The correct way to do it is to define the User serializer in the Django Configuration/Settings file, so that you can retrieve the info you want when you log in.

REST_KNOX = {
    'USER_SERIALIZER': 'auth.serializers.UserRetrieveSerializer'
}

Then simply extend the default login view and use basic auth as authentication class.

from knox.views import LoginView as KnoxLoginView
from rest_framework.authentication import BasicAuthentication
from rest_framework.permissions import IsAuthenticated

class LoginAPI(KnoxLoginView):
    """
    Login endpoint.
    """
    authentication_classes = [BasicAuthentication, ]
    permission_classes = [IsAuthenticated, ]

And for all the other views you could use knox.auth.TokenAuthentication. And that's it...

Relevant code for testing it is:

import base64

def get_basic_auth_header(username, password):
    return 'Basic %s' % base64.b64encode(
        ('%s:%s' % (username, password)).encode('ascii')).decode()

 from rest_framework.test import APITestCase

 class MyTestCase(APITestCase):
     username = 'foo'
     password = 'bar'

     def test_login(self):
         # Add basic auth credentials
         self.client.credentials(
             HTTP_AUTHORIZATION=get_basic_auth_header(
                 self.username, self.password))
         etc...

In your urls.py

from knox import views as knox_views
from .views import LoginAPI

...
    url(r'^login/$', LoginAPI.as_view(), name='knox_login'),                    
    url(r'^logout/$', knox_views.LogoutView.as_view(), name='knox_logout'),     
    url(r'^logoutall/$', knox_views.LogoutAllView.as_view(), name='knox_logoutall'),
...


3 Comments

Thanks. I added permission_classes = [AllowAny, ] but nothing changed. I've tested before.
yes you actually needed both, extend KnoxLoginView and add AllowAny.
Can you elaborate? Did you register your view in the urls.py file? Do you need a code snippet for basic auth in javascript?
0

Where do you get your error "credentials were not provided"? If it is the case when calling the route by hand in the browser, I think it is fine (at least I get it, while data is loaded properly in my tables etc.)

I had the problem that authentication with knox token did not work on Safari and on iPad. The reason was that I had a trailing slash in my routes and this caused 301 - Moved permanently responses with subsequent redirects, which obviously did not forward the token. I removed the trailing slashes and everything was fine then.

By the way: Using

permission_classes = (permissions.AllowAny,)

does not seem to be right, I think it more or less contradicts the use of tokens. It should be

permission_classes = [permissions.IsAuthenticated]

Comments

0

If you are deeploying to the Apache then add the WSGIPassAuthorization On to the 000-default.conf file. That solved my issues. See: https://www.django-rest-framework.org/api-guide/authentication/

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.