Skip to main content
Advertisement

Django Authentication System

Django provides session-based authentication out of the box. SimpleJWT adds JWT token authentication.


Custom User Model

# users/models.py
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin
from django.db import models


class UserManager(BaseUserManager):
def create_user(self, email: str, password: str = None, **extra_fields):
if not email:
raise ValueError("Email is required")
email = self.normalize_email(email)
user = self.model(email=email, **extra_fields)
user.set_password(password)
user.save(using=self._db)
return user

def create_superuser(self, email: str, password: str, **extra_fields):
extra_fields.setdefault("is_staff", True)
extra_fields.setdefault("is_superuser", True)
return self.create_user(email, password, **extra_fields)


class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(unique=True)
name = models.CharField(max_length=150)
phone = models.CharField(max_length=20, blank=True)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_email_verified = models.BooleanField(default=False)
date_joined = models.DateTimeField(auto_now_add=True)

objects = UserManager()

USERNAME_FIELD = "email"
REQUIRED_FIELDS = ["name"]

class Meta:
db_table = "users"
# settings.py
AUTH_USER_MODEL = "users.User" # Must be set before first migrate!

JWT Authentication — SimpleJWT

pip install djangorestframework-simplejwt
# settings.py
from datetime import timedelta

SIMPLE_JWT = {
"ACCESS_TOKEN_LIFETIME": timedelta(minutes=30),
"REFRESH_TOKEN_LIFETIME": timedelta(days=7),
"ROTATE_REFRESH_TOKENS": True,
"BLACKLIST_AFTER_ROTATION": True,
"ALGORITHM": "HS256",
"AUTH_HEADER_TYPES": ("Bearer",),
}
# users/urls.py
from rest_framework_simplejwt.views import (
TokenObtainPairView,
TokenRefreshView,
TokenVerifyView,
TokenBlacklistView,
)

urlpatterns = [
path("login/", TokenObtainPairView.as_view(), name="token_obtain_pair"),
path("refresh/", TokenRefreshView.as_view(), name="token_refresh"),
path("verify/", TokenVerifyView.as_view(), name="token_verify"),
path("logout/", TokenBlacklistView.as_view(), name="token_blacklist"),
path("register/", views.RegisterView.as_view(), name="register"),
path("me/", views.CurrentUserView.as_view(), name="me"),
]

Custom JWT Payload

from rest_framework_simplejwt.serializers import TokenObtainPairSerializer


class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
@classmethod
def get_token(cls, user):
token = super().get_token(user)

# Add extra info to token
token["email"] = user.email
token["name"] = user.name
token["role"] = "admin" if user.is_staff else "user"

return token

Register / Current User API

from rest_framework import generics, status, permissions
from rest_framework.response import Response
from rest_framework.views import APIView
from django.contrib.auth import get_user_model

User = get_user_model()


class RegisterView(generics.CreateAPIView):
"""POST /auth/register/"""
queryset = User.objects.all()
serializer_class = UserRegisterSerializer
permission_classes = [permissions.AllowAny]


class CurrentUserView(generics.RetrieveUpdateAPIView):
"""GET/PUT /auth/me/"""
serializer_class = UserSerializer
permission_classes = [permissions.IsAuthenticated]

def get_object(self):
return self.request.user


class ChangePasswordView(APIView):
"""POST /auth/change-password/"""
permission_classes = [permissions.IsAuthenticated]

def post(self, request):
serializer = ChangePasswordSerializer(data=request.data)
serializer.is_valid(raise_exception=True)

user = request.user
if not user.check_password(serializer.validated_data["old_password"]):
return Response(
{"error": "Current password is incorrect"},
status=status.HTTP_400_BAD_REQUEST,
)

user.set_password(serializer.validated_data["new_password"])
user.save()
return Response({"message": "Password changed successfully"})

Social Login (django-allauth)

pip install django-allauth
# settings.py
INSTALLED_APPS = [
...
"django.contrib.sites",
"allauth",
"allauth.account",
"allauth.socialaccount",
"allauth.socialaccount.providers.google",
"allauth.socialaccount.providers.github",
]

SITE_ID = 1
ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_AUTHENTICATION_METHOD = "email"
ACCOUNT_USERNAME_REQUIRED = False

Summary

ComponentRole
AbstractBaseUserCustom User model base
AUTH_USER_MODELSpecify custom model (before first migrate)
SimpleJWTJWT auth (Access + Refresh Token)
ROTATE_REFRESH_TOKENSIssue new token on refresh
TokenObtainPairSerializerCustomize JWT payload
django-allauthSocial login (Google, GitHub, etc.)

AUTH_USER_MODEL must be configured before the first migration. Changing it afterward is very complex.

Advertisement