A Django app is monolith, not a microservice, by default.
A microservice is a separately deployed Django project with its own auth boundary.
Below is the simplest, correct way to do this using JWT shared between two Django services.
Service 1, Auth Service
This is a standalone Django project whose only job is to issue JWTs.
settings.py
DJANGO_SECRET_KEY = "same-secret-between-all-microservices"
auth_service/auth.py
import jwt
import time
from django.conf import settings
JWT_ALGORITHM = "HS256"
JWT_EXP_SECONDS = 300
def issue_token(user_id: str) -> str:
payload = {
"sub": user_id,
"iat": int(time.time()),
"exp": int(time.time()) + JWT_EXP_SECONDS,
}
token = jwt.encode(
payload,
settings.DJANGO_SECRET_KEY,
algorithm=JWT_ALGORITHM,
)
return token
views.py
from django.http import JsonResponse
from .auth import issue_token
def login(request):
user_id = "123" # assume validated user
token = issue_token(user_id)
return JsonResponse({"access_token": token})
This service only issues jwt tokens. When a user sends email and password to login the correct user.
Service 2, Business Service
This is a separate Django project, deployed independently.
Its database doesn't have users or auth related stuff.
It only knows how to read the content of auth service JWT token because of same django_secret_key.
settings.py
DJANGO_SECRET_KEY = "same-secret-between-all-microservices"
Custom JWT Middleware
middleware.py
import jwt
from django.conf import settings
from django.http import JsonResponse
class JWTAuthMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
auth_header = request.headers.get("Authorization")
if not auth_header or not auth_header.startswith("Bearer "):
return JsonResponse({"detail": "Unauthorized"}, status=401)
token = auth_header.split(" ")[1]
try:
payload = jwt.decode(
token,
settings.DJANGO_SECRET_KEY,
algorithms=["HS256"],
)
except jwt.ExpiredSignatureError:
return JsonResponse({"detail": "Token expired"}, status=401)
except jwt.InvalidTokenError:
return JsonResponse({"detail": "Invalid token"}, status=401)
request.user_id = payload["sub"]
return self.get_response(request)
Register Middleware
settings.py
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"your_app.middleware.JWTAuthMiddleware",
]
Protected View in Service 2
from django.http import JsonResponse
def dashboard(request):
return JsonResponse({
"message": "Access granted",
"user_id": request.user_id,
})
How the services talk
- Client calls Auth Service → receives JWT
- Client calls Business Service with
Authorization: Bearer <token> - Business Service validates token locally
- No shared DB, no shared sessions, no Django apps crossing boundaries
That separation is what makes it a microservice. Separate apps, separate databases, linked together based on good old keys.
The important clarification
- Multiple Django apps in one project is not microservices
- One Django project per service is the minimum bar
- Auth must be explicit and stateless
- Token validation happens in every service
That is the real line between a Django app and a Django microservice.
Top comments (0)