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
Add to settings.py
INSTALLED_APPS = [ 'rest_framework', ] REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework_simplejwt.authentication.JWTAuthentication', ), }
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"), ]
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
Then wire into views:
from rest_framework_simplejwt.views import TokenObtainPairView class CustomTokenObtainPairView(TokenObtainPairView): serializer_class = CustomTokenObtainPairSerializer
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})
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)