DEV Community

Cover image for JWT in Django Rest Framework: Building Secure Auth from Scratch
Zabby
Zabby

Posted on

JWT in Django Rest Framework: Building Secure Auth from Scratch

Why I moved beyond Django’s default auth system and how JWT unlocked modular, secure, and scalable authentication.

Why Not Django’s Default Login?

Let’s face it session-based auth is fine for traditional sites, but once I started building SPAs with React, things got clunky. I needed:

  • Stateless auth for mobile + frontend clients
  • Token refresh flow
  • Endpoint flexibility

Enter JSON Web Tokens (JWT) my passport to modern Django authentication.


Setup with Django Rest Framework + djangorestframework-simplejwt

Installation

pip install djangorestframework djangorestframework-simplejwt
Enter fullscreen mode Exit fullscreen mode

Add to settings.py

INSTALLED_APPS = [
    'rest_framework',
]

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_simplejwt.authentication.JWTAuthentication',
    ),
}
Enter fullscreen mode Exit fullscreen mode

Core JWT Views

DRF SimpleJWT gives you these out of the box:

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"),
]
Enter fullscreen mode Exit fullscreen mode

But I prefer customizing TokenObtainPairSerializer to include user data in the token response.


Custom Serializer Example

Let’s enrich token responses:

from rest_framework_simplejwt.serializers import TokenObtainPairSerializer

class CustomTokenObtainPairSerializer(TokenObtainPairSerializer):
    @classmethod
    def get_token(cls, user):
        token = super().get_token(user)
        token['username'] = user.username
        token['role'] = user.role  # assuming custom field
        return token

    def validate(self, attrs):
        data = super().validate(attrs)
        data['user'] = {
            'username': self.user.username,
            'email': self.user.email
        }
        return data
Enter fullscreen mode Exit fullscreen mode

Then wire into views:

from rest_framework_simplejwt.views import TokenObtainPairView

class CustomTokenObtainPairView(TokenObtainPairView):
    serializer_class = CustomTokenObtainPairSerializer
Enter fullscreen mode Exit fullscreen mode

Auth-Protected Views

Using IsAuthenticated in any DRF view:

from rest_framework.permissions import IsAuthenticated

class ProfileView(APIView):
    permission_classes = [IsAuthenticated]

    def get(self, request):
        return Response({"user": request.user.username})
Enter fullscreen mode Exit fullscreen mode

The frontend just sends the token in the Authorization header:

Authorization: Bearer <your_jwt_token>


Teaching JWT Auth: My Favorite Metaphors

When explaining JWT to students, I use:

  • Passport Analogy: Token carries your identity; the server doesn’t need to remember you.

  • Sticker System: Each JWT is like a sticker pack with claims; don’t trade stickers with strangers.

  • Renewable Visa: Refresh tokens = long-term travel permits.

The goal is to demystify headers, expiry, and token rotation.


Final Thoughts

JWT turned my Django APIs into elegant, frontend-friendly systems. Stateless auth brings clarity, speed, and trust and when structured right, it’s modular, secure, and a joy to maintain.

Top comments (0)