Djoser for DRF with Knox tokens
Asked Answered
O

1

3

I'm trying to use djoser with token authentication, but using django-rest-knox tokens.

I have set the TOKEN_MODEL to knox.models.AuthToken, and the rest framework's DEFAULT_AUTHENTICATION_CLASSES to knox.auth.TokenAuthentication.

I naïvely thought that this would be enough, but it seems that Djoser's inbuilt serializers (create token, and token), don't work properly with the knox tokens. I tried overriding them with custom serializers, but I didn't get anywhere (which is not to say it's not possible, just that I'm bad at this).

It occurred to me that perhaps I should try using Knox's own login views... Is that possible, or can they not be mixed like that? (I'm mainly asking because I don't want to get it to 'work', but find that I've actually introduced a security hole in doing so).

Settings:

DJOSER = {
    "TOKEN_MODEL": "knox.models.AuthToken",
    "SERIALIZERS": {"token": "users.serializers.TokenSerializer"},
}

Where users.serializers.TokenSerializer is:

class TokenSerializer(serializers.ModelSerializer):
    auth_token = serializers.CharField(source="token_key")

    class Meta:
        model = settings.TOKEN_MODEL
        fields = ("auth_token",)

This is only slightly modified from the original Djoser TokenSerializer. It was throwing an error that AuthToken objects did not have a key attribute. Knox tokens seem to call it token_key, so I replaced the line: auth_token = serializers.CharField(source="key") with auth_token = serializers.CharField(source="token_key")

Now, it doesn't throw an error, but it returns an empty token. Inspecting the actual db shows that it has saved a token with the correct user and creation time, but with 'null' for digest, salt, and token_key

Overtly answered 2/3, 2019 at 16:39 Comment(6)
Do you overwrite in Djoser's namespace from your project settings. Serializer should overwrite under Serializer namespace in Djsoer settings.Godman
Yes, I did thatOvertly
can you please add your settings.py, i believe you did that appropriately but just want to check.Godman
@Godman I've added some more detailsOvertly
what is settings.TOKEN_MODEL stands for, i mean is it knox.models.AuthToken or rest_framework.authtoken.models.Token.Godman
what is the status of your problem now ? I am really wondering how you solve your problem.Godman
G
1

Yes, it is possible to mixin's Djoser's and knox's additional view point. For that we are going to create an app name auth from where we are going to serve all authenticational related end-points. Now our project structure is like

MainProject
   -auth
      --__init__.py
      --urls.py
    -mainapp
    ....

Now in our auth app's urls we are going to serve our necessary end-points for authentication. For that we are going to take help from Djoser's urls link and Knox's urls link And our auth's urls.py will be like following

from django.conf.urls import url, include
from django.contrib.auth import get_user_model

from djoser import views as djsoer_views
from knox import views as knox_views

from rest_framework.routers import DefaultRouter

router = DefaultRouter()
router.register('users', djsoer_views.UserViewSet)

User = get_user_model()

djoser_urlpatterns = [
    url(
        r'^users/create/?$',
        djsoer_views.UserCreateView.as_view(),
        name='user-create'
    ),
    url(
        r'^users/delete/?$',
        djsoer_views.UserDeleteView.as_view(),
        name='user-delete'
    ),
    url(
        r'^users/activate/?$',
        djsoer_views.ActivationView.as_view(),
        name='user-activate'
    ),
    url(
        r'^{0}/?$'.format(User.USERNAME_FIELD),
        djsoer_views.SetUsernameView.as_view(),
        name='set_username'
    ),
    url(r'^password/?$', djsoer_views.SetPasswordView.as_view(), name='set_password'),
    url(
        r'^password/reset/?$',
        djsoer_views.PasswordResetView.as_view(),
        name='password_reset'
    ),
    url(
        r'^password/reset/confirm/?$',
        djsoer_views.PasswordResetConfirmView.as_view(),
        name='password_reset_confirm'
    ),
    url(r'^$', djsoer_views.RootView.as_view(), name='root'),
    url(r'^', include(router.urls)),   ### If you want to add user view set
]

knox_urlpatterns = [
    url(r'login/', knox_views.LoginView.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'),
]

urlpatterns = knox_urlpatterns + djoser_urlpatterns

Now we are going to add this urls under our main_app's urls

from django.urls import path
from django.conf import settings
auth_urls = include('auth.urls')

urlpatterns = [
    path('api/auth/', auth_urls),
    ......

]

Now we are going to able to access every end-point like login as api/auth/login/ or user-create as api/auth/user/create/ etc.

What i can see that djoser add some additional UserViewset end-point by default mostly you may not like this , you should include what is really needed for you.
Godman answered 2/3, 2019 at 18:11 Comment(2)
Awesome explanation @Shakil.Paraphernalia
But it's really difficult to make Djoser work with Knox, at least for me :) I've tried a couple of times but as soon as I install Knox, Djoser endpoint which was working earlier for me (register user) stops working. I start getting page not found for every Djoser endpoint immediately after installing Knox. Can someone suggest anything on this?Paraphernalia

© 2022 - 2024 — McMap. All rights reserved.