Analytics Vidhya
Published in

Analytics Vidhya

JWT(JSON Web Token) With DRF

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 models
USER_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 NewsContent
admin.site.register(NewsContent)

Create a supuruser.

$ python manage.py createsuperuser
Username: reporter
Email address: reporter@example.com
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, path
urlpatterns = [
....
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 NewsContentSerializer
class 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 NewsContentViewSet
router = 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/.

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 PermissionDenied
from .models import NewsContent
from .serializers import NewsContentSerializer
class IsReporter(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
return obj.reporter == request.user
class 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 NewsContent
class 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 UserCreateSerializer
User = 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 registration
urlpatterns = [
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_view
schema_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 registration
urlpatterns = [
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

--

--

--

Analytics Vidhya is a community of Analytics and Data Science professionals. We are building the next-gen data science ecosystem https://www.analyticsvidhya.com

Recommended from Medium

Selenium: Growing Muscles

Competence, Clarity and everything in between

Create a simple form in Django 2.0 in 10 steps

80/20 Rule? How It Works For Programmers

What My First Hackathon Taught Me About Product Management

Oracle VirtualBox : Windows 10 on Windows 10

Verification Api | Sms Verification Api | Phone Number Verification Api — fonada

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Erdi MOLLAHÜSEYİNOĞLU

Erdi MOLLAHÜSEYİNOĞLU

Senior Backend Engineer

More from Medium

contractsPY — Python library for handling business transactions with railway-oriented approach…

Retrieving images using database and media in Django

User Auth with REST API in Django — Part 1

Deploy a MVP Django/React Web Application to Digital Ocean