How does django generate unique user token

I'm a bit confused about token authentication. After a few question and attempts I've manage to create url gateway for auto-login my users but I'm managing to do that only by using user_id and passing it out in the url for example http://example.com/auth/login/?user_id=12 but I would rather do it with ?token=.

I'm using DRF example on how make custom auth token and return some more data about my user with that so after I curl to the url I'm returning

{"token":"d5d86e55fd5ddd48298b2ac72c3ed96b7e30dd86","user_id":52}

Now the problem that I'm facing is this MyUser maching query does not exists which is normal I didn't have token in my model so I've created one

token = models.CharField(max_length=125, null=True, blank=True)

so I could overcome DoesNotExist error but the error is still there.

I'm using this hack for the gateway login

from django.contrib.auth import authenticate, login
from django.core.urlresolvers import reverse
from django.views.generic import View
from django.http import HttpResponseRedirect

from business_accounts.models.my_user import MyUser


class UrlGatewayLogin(View):

    def get(self, request, **kwargs):
        page_group = kwargs.get('page_group')
        token = request.GET.get('token')
        user = MyUser.objects.get(token=token)
        user.backend = 'django.contrib.auth.backends.ModelBackend'
        login(request, user)

        return HttpResponseRedirect(reverse('dashboard', args=(page_group, )))

Is DRF token unique per user, and how can I generate token in django and use it for my gateway?

1 answer

  • answered 2018-05-16 06:14 Gnoliz

    You need to install Django-Rest-Framework and enable TokenAuthentication in the settings.

    First you have to write the a serializer for your User Model, and then create an api view with the serialized data for token authentication.

    For example (You can also use username instead of email): In your users/api/serializers.py file:

    from django.utils.translation import ugettext_lazy as _
    
    from rest_framework import serializers
    from rest_framework.compat import authenticate
    
    from ..models import User
    
    
    class AuthTokenSerializer(serializers.Serializer):
        email = serializers.EmailField(label=_("Email Address"))
        password = serializers.CharField(
            label=_("Password"),
            style={'input_type': 'password'},
            trim_whitespace=False
        )
    
        def validate(self, data):
            email = data.get('email')
            password = data.get('password')
    
            if email and password:
                user = authenticate(email=email, password=password)
    
                if user:
                    if not user.is_active:
                        msg = _('User account is deactivated.')
                        raise serializers.ValidationError(msg)
                else:
                    msg = _('Unable to log in with provided credentials.')
                    raise serializers.ValidationError(msg)
            else:
                msg = _('Must include "email" and "password".')
                raise serializers.ValidationError(msg)
    
            data['user'] = user
            return data
    

    Then in your users/api/views.py file:

    from rest_framework.response import Response
    
    from .serializers import AuthTokenSerializer
    from ..models import User
    
    
    class ObtainAuthToken(APIView):
        throttle_classes = ()
        permission_classes = ()
        parser_classes = (parsers.FormParser, parsers.MultiPartParser, parsers.JSONParser,)
        renderer_classes = (renderers.JSONRenderer,)
        serializer_class = AuthTokenSerializer
    
        def post(self, request, *args, **kwargs):
            serializer = self.serializer_class(data=request.data,
                                               context={'request': request})
            serializer.is_valid(raise_exception=True)
            user = serializer.validated_data['user']
            token, created = Token.objects.get_or_create(user=user)
            return Response({'token': token.key, 'username':user.username})
    

    Then in settings, make sure you have token authentication turned on as such:

    REST_FRAMEWORK = {
        'DEFAULT_RENDERER_CLASSES': (
            'rest_framework.renderers.JSONRenderer',
        ),
        'DEFAULT_AUTHENTICATION_CLASSES': (
            'rest_framework.authentication.TokenAuthentication',
        ),
        'DEFAULT_PERMISSION_CLASSES': (
            'rest_framework.permissions.IsAuthenticated',
        )
    }
    

    In order to return data about the user with that token, you need to write another serializer on the User model including the fields you want to return, for instance:

    class UserDetailSerializer(serializers.ModelSerializer):
        class Meta:
            model = User
            fields = [
                'username', 'email', 'hobbies', 'language',
                'gender', 'birthday'
            ]
    

    And write another api view such as:

    class UserDetailAPIView(RetrieveAPIView):
        permission_classes = [IsAuthenticated]
        serializer_class = UserDetailSerializer
    
        def get_object(self):
            obj = get_object_or_404(User, username=self.request.user.username)
            return obj
    

    The key is to write serializers for your model so that the data can be transported on the Internet.