How to implement Token Authentication using Django Rest Framework

How to implement Token Authentication using Django Rest Framework

In this tutorial you are going to learn how to implement Token-based authentication using Django REST Framework (DRF). The token authentication works by exchanging username and password for a token that will be used in all subsequent requests so to identify the user on the server side.

In this tutorial you are going to learn how to implement Token-based authentication using Django REST Framework (DRF). The token authentication works by exchanging username and password for a token that will be used in all subsequent requests so to identify the user on the server side.

The specifics of how the authentication is handled on the client side vary a lot depending on the technology/language/framework you are working with. The client could be a mobile application using iOS or Android. It could be a desktop application using Python or C++. It could be a Web application using PHP or Ruby.

But once you understand the overall process, it’s easier to find the necessary resources and documentation for your specific use case.

Token authentication is suitable for client-server applications, where the token is safely stored. You should never expose your token, as it would be (sort of) equivalent of a handing out your username and password.

Table of Contents

Setting Up The REST API Project

So let’s start from the very beginning. Install Django and DRF:

pip install django
pip install djangorestframework

Create a new Django project:

django-admin.py startproject myapi .

Navigate to the myapi folder:

cd myapi

Start a new app. I will call my app core:

django-admin.py startapp core

Here is what your project structure should look like:

myapi/
 |-- core/
 |    |-- migrations/
 |    |-- __init__.py
 |    |-- admin.py
 |    |-- apps.py
 |    |-- models.py
 |    |-- tests.py
 |    +-- views.py
 |-- __init__.py
 |-- settings.py
 |-- urls.py
 +-- wsgi.py
manage.py

Add the core app (you created) and the rest_framework app (you installed) to the INSTALLED_APPS, inside the settings.py module:

myapi/settings.py

INSTALLED_APPS = [
    # Django Apps
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    # Third-Party Apps
    'rest_framework',

    # Local Apps (Your project's apps)
    'myapi.core',
]

Return to the project root (the folder where the manage.py script is), and migrate the database:

python manage.py migrate

Let’s create our first API view just to test things out:

myapi/core/views.py

from rest_framework.views import APIView
from rest_framework.response import Response

class HelloView(APIView):
    def get(self, request):
        content = {'message': 'Hello, World!'}
        return Response(content)

Now register a path in the urls.py module:

myapi/urls.py

from django.urls import path
from myapi.core import views

urlpatterns = [
    path('hello/', views.HelloView.as_view(), name='hello'),
]

So now we have an API with just one endpoint /hello/ that we can perform GET requests. We can use the browser to consume this endpoint, just by accessing the URL http://127.0.0.1:8000/hello/:

We can also ask to receive the response as plain JSON data by passing the format parameter in the querystring like http://127.0.0.1:8000/hello/?format=json:

Both methods are fine to try out a DRF API, but sometimes a command line tool is more handy as we can play more easily with the requests headers. You can use cURL, which is widely available on all major Linux/macOS distributions:

curl http://127.0.0.1:8000/hello/

But usually I prefer to use HTTPie, which is a pretty awesome Python command line tool:

http http://127.0.0.1:8000/hello/

Now let’s protect this API endpoint so we can implement the token authentication:

myapi/core/views.py

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated  # <-- Here

class HelloView(APIView):
    permission_classes = (IsAuthenticated,)             # <-- And here

    def get(self, request):
        content = {'message': 'Hello, World!'}
        return Response(content)

Try again to access the API endpoint:

http http://127.0.0.1:8000/hello/

And now we get an HTTP 403 Forbidden error. Now let’s implement the token authentication so we can access this endpoint.

Implementing the Token Authentication

We need to add two pieces of information in our settings.py module. First include rest_framework.authtoken to your INSTALLED_APPS and include the TokenAuthentication to REST_FRAMEWORK:

myapi/settings.py

INSTALLED_APPS = [
    # Django Apps
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    # Third-Party Apps
    'rest_framework',
    'rest_framework.authtoken',  # <-- Here

    # Local Apps (Your project's apps)
    'myapi.core',
]

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.TokenAuthentication',  # <-- And here
    ],
}

Migrate the database to create the table that will store the authentication tokens:

python manage.py migrate

Now we need a user account. Let’s just create one using the manage.py command line utility:

python manage.py createsuperuser --username vitor --email [email protected]

The easiest way to generate a token, just for testing purpose, is using the command line utility again:

python manage.py drf_create_token vitor

This piece of information, the random string 9054f7aa9305e012b3c2300408c3dfdf390fcddf is what we are going to use next to authenticate.

But now that we have the TokenAuthentication in place, let’s try to make another request to our /hello/ endpoint:

http http://127.0.0.1:8000/hello/

Notice how our API is now providing some extra information to the client on the required authentication method.

So finally, let’s use our token!

http http://127.0.0.1:8000/hello/ 'Authorization: Token 9054f7aa9305e012b3c2300408c3dfdf390fcddf'

And that’s pretty much it. For now on, on all subsequent request you should include the header Authorization: Token 9054f7aa9305e012b3c2300408c3dfdf390fcddf.

The formatting looks weird and usually it is a point of confusion on how to set this header. It will depend on the client and how to set the HTTP request header.

For example, if we were using cURL, the command would be something like this:

curl http://127.0.0.1:8000/hello/ -H 'Authorization: Token 9054f7aa9305e012b3c2300408c3dfdf390fcddf'

Or if it was a Python requests call:

import requests

url = 'http://127.0.0.1:8000/hello/'
headers = {'Authorization': 'Token 9054f7aa9305e012b3c2300408c3dfdf390fcddf'}
r = requests.get(url, headers=headers)

Or if we were using Angular, you could implement an HttpInterceptor and set a header:

import { Injectable } from '@angular/core';
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const user = JSON.parse(localStorage.getItem('user'));
    if (user && user.token) {
      request = request.clone({
        setHeaders: {
          Authorization: `Token ${user.accessToken}`
        }
      });
    }
    return next.handle(request);
  }
}
User Requesting a Token

The DRF provide an endpoint for the users to request an authentication token using their username and password.

Include the following route to the urls.py module:

myapi/urls.py

from django.urls import path
from rest_framework.authtoken.views import obtain_auth_token  # <-- Here
from myapi.core import views

urlpatterns = [
    path('hello/', views.HelloView.as_view(), name='hello'),
    path('api-token-auth/', obtain_auth_token, name='api_token_auth'),  # <-- And here
]

So now we have a brand new API endpoint, which is /api-token-auth/. Let’s first inspect it:

http http://127.0.0.1:8000/api-token-auth/

It doesn’t handle GET requests. Basically it’s just a view to receive a POST request with username and password.

Let’s try again:

http post http://127.0.0.1:8000/api-token-auth/ username=vitor password=123

The response body is the token associated with this particular user. After this point you store this token and apply it to the future requests.

Then, again, the way you are going to make the POST request to the API depends on the language/framework you are using.

If this was an Angular client, you could store the token in the localStorage, if this was a Desktop CLI application you could store in a text file in the user’s home directory in a dot file.

Conclusions

Hopefully this tutorial provided some insights on how the token authentication works. I will try to follow up this tutorial providing some concrete examples of Angular applications, command line applications and Web clients as well.

It is important to note that the default Token implementation has some limitations such as only one token per user, no built-in way to set an expiry date to the token.

You can grab the code used in this tutorial at github.com/sibtc/drf-token-auth-example.

Angular 9 Tutorial: Learn to Build a CRUD Angular App Quickly

What's new in Bootstrap 5 and when Bootstrap 5 release date?

What’s new in HTML6

How to Build Progressive Web Apps (PWA) using Angular 9

What is new features in Javascript ES2020 ECMAScript 2020

Top Python Development Companies | Hire Python Developers

Top Python Development Companies | Hire Python Developers

After analyzing clients and market requirements, TopDevelopers has come up with the list of the best Python service providers. These top-rated Python developers are widely appreciated for their professionalism in handling diverse projects. When...

After analyzing clients and market requirements, TopDevelopers has come up with the list of the best Python service providers. These top-rated Python developers are widely appreciated for their professionalism in handling diverse projects. When you look for the developer in hurry you may forget to take note of review and ratings of the company's aspects, but we at TopDevelopers have done a clear analysis of these top reviewed Python development companies listed here and have picked the best ones for you.

List of Best Python Web Development Companies & Expert Python Programmers.

Python Django Tutorial | Django Course

Python Django Tutorial | Django Course

🔥Intellipaat Django course: https://intellipaat.com/python-django-training/ 👉This Python Django tutorial will help you learn what is django web development &...

This Python Django tutorial will help you learn what is django web development & application, what is django and introduction to django framework, how to install django and start programming, how to create a django project and how to build django app. There is a short django project as well to master this python django framework.

Why should you watch this Django tutorial?

You can learn Django much faster than any other programming language and this Django tutorial helps you do just that. Our Django tutorial has been created with extensive inputs from the industry so that you can learn Django and apply it for real world scenarios.

JWT with Django REST Framework to Authenticate Users

JWT with Django REST Framework to Authenticate Users

In this article we’ll use JWT (JSON Web Token) to authenticate users that JWT using with Django REST Framework. When the news content is created, only current logged in a user can perform read and write operations to his own news contents.

Section Overview
  • build django project.
  • define the news content model and admin.
  • configure news content api.
  • authentication and permissions.
  • adding JWT(JSON Web Token).
  • swagger docs.

JSON Web Token is an Token based authentication used by client/server applications where the client is a web, native, mobile or something else application using. JWT is a popular implementation of token based authentication.

In this article we’ll use it to authenticate users that JWT using with Django REST Framework. When the news content is created, only current logged in a user can perform read and write operations to his own news contents.

Section 1. Build Django Project

Firstly, install Django, Django REST Framework(DRF) and django-cors-headers (CORS).

$ pip install django django-rest-framework django-cors-headers

Then, create a new project folder (news_contents/).

$ mkdir news_contents
$ cd news_contents

Create venv(virtual environment) and activate it.

$ python -m venv venv
$ source venv/bin/activate

Now, build a django project (core).

$ django-admin startproject core
$ cd core

Create a newscontents app.

$ python manage.py startapp newscontents
$ python manage.py migrate

We will now configure our django project to use CORS and DRF in core/settings.py.

# Application definitionINSTALLED_APPS = [
    ....
    'rest_framework',
    'corsheaders',
    'newscontents',
]MIDDLEWARE = [
    ....
    'corsheaders.middleware.CorsMiddleware',
]CORS_ORIGIN_ALLOW_ALL = TrueREST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES':['rest_framework.permissions.AllowAny'],
    'DEFAULT_PARSER_CLASSES': ['rest_framework.parsers.JSONParser'],
}

Let’s run the server.

$ python manage.py runserver
Watching for file changes with StatReloader
Performing system checks...System check identified no issues (0 silenced).
November 08, 2019 - 07:24:30
Django version 2.2.7, using settings 'core.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
Section 2. Define The NewsContent Model And Admin

Create NewsContent Model in core/newscontents/models.py.

from django.conf import settings
from django.contrib.auth import get_user_model
from django.db import modelsUSER_MODEL = get_user_model()class NewsContent(models.Model):
    reporter = models.ForeignKey(
        to=USER_MODEL,
        on_delete=models.CASCADE,
        related_name='news_contents',
    )
    headline = models.CharField(max_length=255)
    body = models.TextField()    def __str__(self):
        return self.headline

Run migrations.

$ python manage.py makemigrations
$ python manage.py migrate

The admin site of NewsContentModel in core/newscontents/admin.py.

from django.contrib import admin
from .models import NewsContentadmin.site.register(NewsContent)

Create a supuruser.

$ python manage.py createsuperuser
Username: reporter
Email address: [email protected]
Password: 
Password (again):
Superuser created successfully.

Let’s start the server again.

$ python manage.py runserver
Watching for file changes with StatReloader
Performing system checks...System check identified no issues (0 silenced).
November 08, 2019 - 07:35:23
Django version 2.2.7, using settings 'core.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
Section 3. Configure News Content API

Add some urls so we can access the news content API in core/urls.py.

....
from django.urls import include, pathurlpatterns = [
    ....
    path('api/', include('newscontents.urls')),
]

Then, create a serializer for NewsContent model in newscontents/serializers.py.

from rest_framework import serializers
from .models import NewsContent
class NewsContentSerializer(serializers.ModelSerializer):
    class Meta:
        fields = ('id', 'reporter', 'headline', 'body')
        model = NewsContent

Next we’ll create the view in newscontents/views.py.

from rest_framework import viewsets
from .models import NewsContent
from .serializers import NewsContentSerializerclass NewsContentViewSet(viewsets.ModelViewSet):
    queryset = NewsContent.objects.all()
    serializer_class = NewsContentSerializer

Create the urls in newscontents/urls.py.

from django.urls import path
from rest_framework.routers import SimpleRouter
from .views import NewsContentViewSetrouter = SimpleRouter()
router.register('news-content', NewsContentViewSet, base_name="news_content")
urlpatterns = router.urls
Section 4. Authentication And Permissions

Firstly, create a new user in the admin panel. Then, add the following line in core/urls.py.

urlpatterns = [
    ....
    path('auth/', include('rest_framework.urls')),
]

Then, run to server and we will create a new user and enter news content from admin panel.

$ python manage.py runserver
Watching for file changes with StatReloader
Performing system checks...System check identified no issues (0 silenced).
November 08, 2019 - 11:08:55
Django version 2.2.7, using settings 'core.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

You can verify this by logging on and then going to [http://localhost:8000/api/news-content/](http://localhost:8000/api/news-content/.).

We have configured rest_framework with AllowAny. So, each users have access to newscontents. Now, we need to change into your core/settings.py file:

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': ["rest_framework.permissions.IsAuthenticated",],
    'DEFAULT_PARSER_CLASSES': ['rest_framework.parsers.JSONParser'],
}

We need to change in newscontents/views.py for ensure a user sees only own News Contents objects and set user as reporter of a News Contents object.

from rest_framework import permissions, viewsets
from rest_framework.exceptions import PermissionDeniedfrom .models import NewsContent
from .serializers import NewsContentSerializerclass IsReporter(permissions.BasePermission):
    def has_object_permission(self, request, view, obj):
        return obj.reporter == request.userclass NewsContentViewSet(viewsets.ModelViewSet):
    serializer_class = NewsContentSerializer
    permission_classes = (IsReporter,)    # Ensure a user sees only own News Content objects.
    def get_queryset(self):
        user = self.request.user
        if user.is_authenticated:
            return NewsContent.objects.filter(reporter=user)
        raise PermissionDenied()    # Set user as owner of a NewsContents object.
    def perform_create(self, serializer):
        serializer.save(owner=self.request.user)

If you visit http://localhost:8000/api/news-content/, you should only see the news contents belonging to the current logged in user. So, we can remove the reporter field from the serializer in newscontents/serializers.py:

from rest_framework import serializers
from .models import NewsContentclass NewsContentSerializer(serializers.ModelSerializer):
    class Meta:
        fields = ('id', 'headline', 'body')
        model = NewsContent

We have configure for authentication and permissions. But DRF using still session authentication. Now we will use Json token authentication(JWT) .

Section 5. Adding JWT(JSON Web Token)

We will use a library (djangorestframework_simplejwt).

$ pip install djangorestframework_simplejwt

We add it to DEFAULT_AUTHENTICATION_CLASSES in core/settings.py.

REST_FRAMEWORK = {
 'DEFAULT_PERMISSION_CLASSES':["rest_framework.permissions.IsAuthenticated"],
    'DEFAULT_PARSER_CLASSES': ['rest_framework.parsers.JSONParser'],
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework_simplejwt.authentication.JWTAuthentication',
       ],
}

Add new end points for token of api and refresh of token in core/urls.py.

from rest_framework_simplejwt.views import TokenObtainView, TokenRefreshViewurlpatterns = [
    ....
    path('api/token/', TokenObtainPairView.as_view(), name='token_obtain'),
    # get a new token before the old expires.
    path('api/token/refresh/', TokenRefreshView.as_view, name='token_refresh'),
]
User SIGN-UP

We need new endpoint for users can sign up. Now create a new app for jwtauth.

$ python manage.py startapp jwtauth

Add app in core/settings.py.

INSTALLED_APPS = [
    ....
    'jwtauth',
]

Then, we’ll create a new serializer for the User model. Add jwtauth/serializers.py.

from django.contrib.auth import get_user_modelfrom rest_framework import serializersUser = get_user_model()
class UserCreateSerializer(serializers.ModelSerializer):
    password = serializers.CharField(
        write_only=True, required=True, style={'input_type': 'password'}
    )
    password2 = serializers.CharField(
        style={'input_type': 'password'}, write_only=True, label='Confirm password'
    )    class Meta:
        model = User
        fields = ['username', 'email', 'password', 'password2']
        extra_kwargs = {'password': {'write_only': True}}    def create(self, validated_data):
        username = validated_data['username']
        email = validated_data['email']
        password = validated_data['password']
        password2 = validated_data['password2']
        if (email and User.objects.filter(email=email).exclude(username=username).exists()
        ):
            raise serializers.ValidationError(
                {'email': 'Email addresses must be unique.'}
            )
        if password != password2:
            raise serializers.ValidationError({'password': 'The two passwords differ.'})
        user = User(username=username, email=email)
        user.set_password(password)
        user.save()
        return user

Then we’ll create a new view in jwtauth/views.py.

from django.contrib.auth import get_user_model
from rest_framework import permissions
from rest_framework import response, decorators, permissions, status
from rest_framework_simplejwt.tokens import RefreshToken
from .serializers import UserCreateSerializerUser = get_user_model()@decorators.api_view(['POST'])
@decorators.permission_classes([permissions.AllowAny])
def registration(request):
    serializer = UserCreateSerializer(data=request.data)
    if not serializer.is_valid():
        return response.Response(serializer.errors, status.HTTP_400_BAD_REQUEST)        
    user = serializer.save()
    refresh = RefreshToken.for_user(user)
    res = {
        'refresh': str(refresh),
        'access': str(refresh.access_token),
    }
    return response.Response(res, status.HTTP_201_CREATED)

We add the view to jwtauth/urls.py.

from django.urls import path
from .views import registrationurlpatterns = [
    path('register/', registration, name='register')
]

Next, include the jwtauth urls in core/urls.py.

urlpatterns = [
    ....
    path('api/jwtauth/', include('jwtauth.urls'), name='jwtauth'),
]
Section 6. Swagger Docs
$ pip install django-rest-swagger

Add it to your INSTALLED_APPS list in core/settings.py.

INSTALLED_APPS = [
    'rest_framework_swagger',
    ....
]REST_FRAMEWORK = {
    ....
 'DEFAULT_SCHEMA_CLASS':'rest_framework.schemas.coreapi.AutoSchema',
}

Add following code core/urls.py.

from django.contrib import admin
from django.urls import path, include
from rest_framework_swagger.views import get_swagger_viewschema_view = get_swagger_view(title="News Contents API")urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/', include('newscontents.urls')),
    path('auth/', include('rest_framework.urls')),
    path('api/jwtauth/', include('jwtauth.urls'), name='jwtauth'),
    path('api/docs/', schema_view),
]

Then put the token and refresh enpoints in jwtauth/urls.py.

from django.urls import path
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView
from .views import registrationurlpatterns = [
    path("register/", registration, name="register"),
    path("token/", TokenObtainPairView.as_view(), name="token_obtain_pair"),
    path("refresh/", TokenRefreshView.as_view(), name="token_refresh"),
]

You can visit to http://localhost:8000/api/docs/ for the full list of APIs and the token endpoints now belong to the /jwtauth grouping.

References