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)