How to use token based authentication in django

In this tutorial, I will show you how to use token-based authentication given by the rest framework in your django project.

Token-based authentication is the protocol that allows the users to verify their identity on the server-side. The first user sends their authentication credentials like username and password to the server and after verifying the information, the server sends back the token that can be used in all upcoming requests to verify the identity of the user.

Luckily Django rest framework provides all the necessary functions and classes right out of the box to use the token-based authentication in our project.

So, let's start by creating a new django project.

Create a new project

pip install django
pip install djangorestframework
django-admin startproject tutorial
cd tutorial
python manage.py startapp core

Edit settings

To use the token-based authentication you need to add the 'rest_framework.authtoken' in the installed apps list and set 'rest_framework.authentication.TokenAuthentication' as the default authentication class inside the project's settings.py file.

tutorial/settings.py
INSTALLED_APPS=[
'rest_framework',
'rest_framework.authtoken',
'core.apps.CoreConfig'
]


REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.TokenAuthentication'
    ]
}

Implementation

Let's create a simple api where the user has to be authenticated to be able to access the data.

core/views.py
from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response

class SecretInfoView(APIView):
    permission_classes = (IsAuthenticated,)

    def get(self, request):
        return Response({"hello": "World"})

Add Urls

Let Django know about the URL routes of the core app.

tutorial/urls.py
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path("api/", include("core.urls"))
]

Now, make a new urls.py file inside the core app and add these two URLs first to get our secret info and the other to get the authentication token.

Note: obtain_auth_token is the function provided by the rest framework that returns the authentication token after verifying the username and password.

core/urls.py
from django.urls import path
from rest_framework.authtoken.views import obtain_auth_token

from core import views 

urlpatterns = [
    path("secret-info/", views.SecretInfoView.as_view()),
    path("get-token/", obtain_auth_token)
]

Now, let's migrate the database and run the server

python manage.py makemigrations
python manage.py migrate
python manage.py createsuperuser
python manage.py runserver

Test the api's

To test the API's I am going to use an awesome tool called 'httpie' that you can quickly install in just one command via pip.

pip install httpie

Try to access the secret info without providing any token.

http http://127.0.0.1:8000/api/secret-info/

HTTP/1.1 401 Unauthorized
...
{
    "detail": "Authentication credentials were not provided."
}

Get the authentication token by providing the valid username and password.

http http://127.0.0.1:8000/api/get-token/ username=shivam password=testing123

HTTP/1.1 200 OK
...
{
    "token": "e920592e7d69121619232e2a24385eacab365316"
}

Finally, get access to the secret information by sending the valid token as the authorization header.

http http://127.0.0.1:8000/api/secret-info/ 'Authorization: Token e920592e7d69121619232e2a24385eacab365316'

HTTP/1.1 200 OK
...
{
    "hello": "World"
}

Own Version of Obtain Token Function

What if you have different authentication requirements or criteria? What if you want to know the implementation get_obtain_function? Well, I got you covered.

So in the below example, I'll show you the simple login view that returns the token after verifying the username and password. But if you have different criteria for verifying the user then modifying this function won't be hard for you.

Let's create the serializer to verify the user input.

core/serializers.py
from rest_framework import serializers

class LoginSerializer(serializers.Serializer):
    username = serializers.CharField(max_length=20)
    password = serializers.CharField(max_length=20)

Here is the login view.

core/views.py
...
from rest_framework import status
from core.serializers import LoginSerializer
from django.contrib.auth import authenticate
from rest_framework.authtoken.models import Token
    
...
class LoginView(APIView):

    def post(self, request):
        serializer = LoginSerializer(data=request.data)
        if serializer.is_valid():
            user = authenticate(
                username=serializer.validated_data['username'], 
                password=serializer.validated_data['password']
            )
            if user is not None:
                token, created = Token.objects.get_or_create(user=user)
                return Response({"token": token.key})
            else:
                return Response({"msg": "Invalid username/password"}, 
                    status=status.HTTP_400_BAD_REQUEST)
        else:
            return Response(serializer.errors, 
                status=status.HTTP_400_BAD_REQUEST)

Finally, Let's connect the login view to a route.

core/urls.py
urlpatterns = [
...
path("login/", views.LoginView.as_view()),
]