sunscrapers / djoser

REST implementation of Django authentication system.
MIT License
2.53k stars 459 forks source link

Using knox tokens with Djoser #362

Open alrhalford opened 5 years ago

alrhalford commented 5 years ago

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).

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

rolep commented 5 years ago

In new project am also starting with djoser + knox.

Currently trying it with the following, and as brief djoser / knox code review showed - it should be ok.

The trick is not to use djoser urls, but instead use knox urls for token auth:


Social authentication so far returns jwt token for the user.

Afaics, social_auth returns already valid / created user, and djoser returns new / refreshed jwt token for him. In case of tokens - you can create other TokenStrategy classes (how social auth works in djoser may change in future).

TokenStrategy examples - from :

class TokenStrategy:

@classmethod
def obtain(cls, user):
    token, created = Token.objects.get_or_create(user=user)
    return Response({'token': token.key})

- using knox token - `knox_token.py`

from django.http import HttpRequest from knox.views import LoginView

class TokenStrategy:

@classmethod
def obtain(cls, user):
    request = HttpRequest()
    request.method = "POST"
    request.user = user
    request._force_auth_user = user
    return LoginView.as_view()(request)

and specify required TokenStrategy in `settings.py`, i.e.:

DJOSER = { 'TOKEN_MODEL': 'knox.models.AuthToken', 'SOCIAL_AUTH_TOKEN_STRATEGY': 'myapp.api.knox_token.TokenStrategy', }

miqsoft commented 9 months ago

If you want to implement djoser social with knox nowadays, additionally to @rolep procedure, it is needed to save the retrieved token in a dictionary, that matches the ProviderAuthSerializer data. Therefore the Tokenstrategy must look like the following:

class TokenStrategy:
    @classmethod
    def obtain(cls, user):
        request = HttpRequest()
        request.method = "POST"
        request.user = user
        request._force_auth_user = user
        val = LoginView.as_view()(request)
        return {
            'access': val.data['token'],
        }