Secure Django REST APIs with JWT Authentication using Simple-JWT

When I first started working with Django REST Framework (DRF), securing APIs was always a critical concern. Over the years, I’ve found JSON Web Tokens (JWT) to be one of the best ways to handle authentication for APIs, especially when scalability and statelessness matter. JWT allows you to transmit user information securely between client and server without the need for server-side sessions.

In this article, I’ll walk you through implementing JWT authentication in a Django REST Framework project. We’ll cover everything from setting up the environment to writing the full code example, using a real-world scenario that resonates with developers.

What is JWT Authentication?

JWT stands for JSON Web Token. It’s a compact, URL-safe means of representing claims to be transferred between two parties. In the context of web APIs, JWT is used to securely transmit user identity information after login, so the server can verify the user on subsequent requests without storing session data.

JWT tokens are self-contained, meaning all the user info and claims are included in the token itself, digitally signed to prevent tampering.

Read Python Django Length Filter

Set Up JWT Authentication in Django REST Framework

I’ll show you two common methods to implement JWT authentication:

  1. Using the popular djangorestframework-simplejwt package.
  2. A manual approach using custom token generation (for learning purposes).

Method 1: Use djangorestframework-simplejwt

This is the most recommended and widely used method. It provides ready-to-use JWT authentication classes and views.

Step 1: Install Required Packages

pip install djangorestframework
pip install djangorestframework-simplejwt
pip install django

Step 2: Update settings.py

Add 'rest_framework' and 'rest_framework_simplejwt.authentication.JWTAuthentication' to your Django settings.

INSTALLED_APPS = [
    ...
    'rest_framework',
]

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_simplejwt.authentication.JWTAuthentication',
    ),
}

Step 3: Define URLs for Token Operations

In your urls.py, add routes for obtaining and refreshing tokens.

from django.urls import path
from rest_framework_simplejwt.views import (
    TokenObtainPairView,
    TokenRefreshView,
)

urlpatterns = [
    path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
    path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
]

Check out Django for Loop

Step 4: Create a Simple API View to Test Authentication

Here’s an example of a protected view that returns the logged-in user’s info:

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

class UserProfileView(APIView):
    permission_classes = [IsAuthenticated]

    def get(self, request):
        user = request.user
        return Response({
            'username': user.username,
            'email': user.email,
        })

Add this view to your URLs:

urlpatterns += [
    path('api/profile/', UserProfileView.as_view(), name='user_profile'),
]

Step 5: Test the JWT Authentication Flow

  1. Obtain Token:
    Send a POST request to /api/token/ with JSON body:
{
  "username": "yourusername",
  "password": "yourpassword"
}
no active account found with the given credentials

You will receive:

{
  "refresh": "your_refresh_token",
  "access": "your_access_token"
}
tokenobtainpairview
  1. Access Protected API:
    Include the access token in the Authorization header:
Authorization: Bearer your_access_token

Call /api/profile/ and you’ll get your user info.

  1. Refresh Token:
    Call /api/token/refresh/ with the refresh token to get a new access token.
django json web token

Read Python Django Group By

Method 2: Manual JWT Implementation (For Learning)

While I recommend using simplejwt for production, understanding how JWT works under the hood is helpful.

Step 1: Install PyJWT

pip install PyJWT

Step 2: Create Token Utility Functions

import jwt
import datetime
from django.conf import settings

def generate_jwt_token(user):
    payload = {
        'user_id': user.id,
        'username': user.username,
        'exp': datetime.datetime.utcnow() + datetime.timedelta(days=1),
        'iat': datetime.datetime.utcnow(),
    }
    token = jwt.encode(payload, settings.SECRET_KEY, algorithm='HS256')
    return token

Step 3: Create a Login API View

from django.contrib.auth import authenticate
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status

class LoginAPIView(APIView):
    def post(self, request):
        username = request.data.get('username')
        password = request.data.get('password')
        user = authenticate(username=username, password=password)

        if user:
            token = generate_jwt_token(user)
            return Response({'token': token})
        return Response({'error': 'Invalid Credentials'}, status=status.HTTP_401_UNAUTHORIZED)

Step 4: Create a Custom Authentication Class

from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from django.contrib.auth.models import User

class JWTAuthentication(BaseAuthentication):
    def authenticate(self, request):
        auth_header = request.headers.get('Authorization')
        if not auth_header:
            return None

        prefix, token = auth_header.split(' ')

        if prefix.lower() != 'bearer':
            return None

        try:
            payload = jwt.decode(token, settings.SECRET_KEY, algorithms=['HS256'])
        except jwt.ExpiredSignatureError:
            raise AuthenticationFailed('Token expired')
        except jwt.InvalidTokenError:
            raise AuthenticationFailed('Invalid token')

        user = User.objects.get(id=payload['user_id'])
        return (user, token)

Step 5: Protect Views Using Custom Authentication

from rest_framework.permissions import IsAuthenticated

class UserProfileView(APIView):
    authentication_classes = [JWTAuthentication]
    permission_classes = [IsAuthenticated]

    def get(self, request):
        user = request.user
        return Response({
            'username': user.username,
            'email': user.email,
        })

Implementing JWT authentication in Django REST Framework is straightforward and essential for building secure, scalable APIs. The djangorestframework-simplejwt package is my go-to solution because it handles the heavy lifting and follows best practices.

If you want to understand JWT deeply, implementing a manual version is a great exercise. However, for production-ready apps, relying on battle-tested libraries ensures security and maintainability.

With JWT, your API can securely authenticate users without server-side sessions, making your backend stateless and easier to scale, perfect for modern web and mobile applications.

You may also read:

51 Python Programs

51 PYTHON PROGRAMS PDF FREE

Download a FREE PDF (112 Pages) Containing 51 Useful Python Programs.

pyython developer roadmap

Aspiring to be a Python developer?

Download a FREE PDF on how to become a Python developer.

Let’s be friends

Be the first to know about sales and special discounts.