Django: User Authentication using knox
Tags: django, python, database, vscode
Published: 2020-11-19 19:49:35

To get started, make sure you have django installed by using the command `pipenv install django` in your terminal. (Make sure you are in a virtual environment: `pipenv shell`)

Create a project `django-admin startproject <project-name>`. For this example, I will be creating a project about tvshows! So, my command will be `django-admin startproject tvshows`.

Now, cd into your project and make sure `manage.py` is in your root directory.

The command `python manage.py runserver` allows your app to be up and running. `http://127.0.0.1:8000/` on your browser, should show this:
![](https://imgur.com/3x3Wu7W.png)

Next, create a superuser:`python manage.py createsuperuser`
This asks for a username, password and email address. Now that you have created a superuser, on the browser, add `/admin` to your initial site like this: `http://127.0.0.1:8000/admin` and your browser should show:
![](https://imgur.com/uqtHB63.png)

Use the username and password used to create the superuser to login! you should then see:
![](https://imgur.com/ePButKF.png)

Now, you are ready to create user auth using knox where a token is generated for a user to login and access data.
You need to install the following:
`pipenv install djangorestframework`
`pipenv install django-rest-knox`

In `settings.py`, add:
```python
INSTALLED_APPS = [
    ...
    'rest_framework',
    'knox'
]
```
Now, let's create an app for our project.
Use `python manage.py startapp <name>` in the terminal. For now, I've used `python manage.py startapp backend`. 

Your files in your code editor should look something like this:
![](https://imgur.com/sAPwy0G.png)

Starting with Registration API:

In the `backend` folder, create a new file and name it `serializers.py` and add:
```python
from rest_framework import serializers
from django.contrib.auth.models import User
from django.contrib.auth import authenticate

class CreateUserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ('id', 'username', 'password')
        extra_kwargs = {'password': {'write_only': True}}

    def create(self, validated_data):
        user = User.objects.create_user(validated_data['username'],
                                        None,
                                        validated_data['password'])
        return user

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ('id', 'username')
```

Then, create an `api.py` file in the `backend` folder and add:
```python
from rest_framework import viewsets, permissions, generics
from rest_framework.response import Response
from knox.models import AuthToken
from .serializers import CreateUserSerializer, UserSerializer

class RegistrationAPI(generics.GenericAPIView):
    serializer_class = CreateUserSerializer

    def post(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        user = serializer.save()
        return Response({
            "user": UserSerializer(user, context=self.get_serializer_context()).data,
            "token": AuthToken.objects.create(user)[1]
        })
```
Now, to add the endpoints, create a `urls.py` file in the `backend` folder and add:
```python
from django.urls import path
from .api import RegistrationAPI
from knox import views as knox_views

urlpatterns = [
    path('register/', RegistrationAPI.as_view()),
]
```
You also need to add endpoint in `urls.py` file in the project folder which is `tvshows` in this case:
```python
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/', include('backend.urls')),
    path('api/auth/', include('knox.urls'))
]
```
Registration API is now complete. Next is creating a Login API.

In the `serializers.py` in your `backend` folder, add:
```python
class LoginUserSerializer(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 Details.")
```
Next, in `api.py` add:
```python
from .serializers import CreateUserSerializer, UserSerializer, LoginUserSerializer

class LoginAPI(generics.GenericAPIView):
    serializer_class = LoginUserSerializer

    def post(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        user = serializer.validated_data
        return Response({
            "user": UserSerializer(user, context=self.get_serializer_context()).data,
            "token": AuthToken.objects.create(user)[1]
        })
```
You need to add the endpoints in `urls.py` in the `backend` folder:
```python
from .api import RegistrationAPI, LoginAPI

urlpatterns = [
    ...,
    path('login/', LoginAPI.as_view()),
]
```
Finally, you need an API to shows the user data when the user successfully logs in. To do this, you need to create a class in `api.py` file in your `backend` folder:
```python
class UserAPI(generics.RetrieveAPIView):
    permission_classes = [permissions.IsAuthenticated, ]
    serializer_class = UserSerializer

    def get_object(self):
        return self.request.user
```
This needs an endpoint too. Therefore, in your `urls.py` in your `backend` folder, add:
```python
from django.urls import path
from .api import RegistrationAPI, LoginAPI, UserAPI
from knox import views as knox_views

urlpatterns = [
    path('register/', RegistrationAPI.as_view()),
    path('login/', LoginAPI.as_view()),
    path('user/', UserAPI.as_view()),
]
```
Now, you're all set. Make sure you run those migrations, using `python manage.py makemigrations` followed by `python manage.py migrate`.

You can use [Postman](https://www.postman.com/) to check if your endpoint work and you're able to do `GET` and `POST` requests.

Let's start with registration. You need to add `header` and a `body` to create a new user. So using the url `http://127.0.0.1:8000/api/register/`, add key `Content-Type` and value `application/json` on the `header` like this:
![](https://imgur.com/kWMnxsR.png)
And add the `body` to `POST` to the API, like this:
![](https://imgur.com/2BbVCVW.png)
Your registration has been successful!

You can now log in using `http://127.0.0.1:8000/api/login/` and `POST method` adding the same `body` used to register. This generates a token:
![](https://imgur.com/SBF9xyE.png)

You cannot access the user details without passing the token that was generated when successfully logging in, as seen below:
![](https://imgur.com/O9ipH0c.png)
You are unable to view the user detail.

Therefore, you need to pass the token like this:
![](https://imgur.com/WicLo7k.png)

You are now able to view the user detail:
![](https://imgur.com/w7MIR0V.png)

You can also see if the user was successfully created on django admin:
![](https://imgur.com/SwXvge6.png)

`bhuma08` was the superuser I created at the beginning and `user1` was created using Postman.

For logout functionality, just add a path in your `backend/urls.py`:
```python
urlpatterns = [
    ...
    path('logout/', knox_views.LogoutView.as_view(), name='knox_logout')
]
```

That's the end! :)