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

Django python security web-development jwt

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

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

What is new features in Javascript ES2020 ECMAScript 2020

Deno Crash Course: Explore Deno and Create a full REST API with Deno

How to Build a Real-time Chat App with Deno and WebSockets

Convert HTML to Markdown Online

HTML entity encoder decoder Online

Random Password Generator Online

HTML Color Picker online | HEX Color Picker | RGB Color Picker

Basic Data Types in Python | Python Web Development For Beginners

In the programming world, Data types play an important role. Each Variable is stored in different data types and responsible for various functions. Python had two different objects, and They are mutable and immutable objects.

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

Django Web Development in Python

Learn about the basics of WEB DEVELOPMENT using DJANGO to build blog applications that have the (CRUD) Create, Read, Update, Delete functionality. Django is a widely used free, open-source, and high-level web development framework. This tutorial doesn't use any FrontEnd technologies like Javascript, CSS, etc. but focuses on basic yet essential concepts that are needed in every Django Web Development.

Top 6 Python Packages you should be using in every Django Web App

While some Python packages offer cool functionality needed for one specific project, the packages discussed below are the bread-and-butter of the Django packages. In this Python tutorial, you'll see Top 6 Python Packages you should be using in every Django Web App

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