Custom User Model Authentication in Django REST Framework
Customizing Django Auth User Model
RBAC (Role-Based Access Control) implements permission management through roles. Django's Auth module employs six core permission tables: User, Group, Permission, and their relationship tables.
# api/models.py
from django.db import models
from django.contrib.auth.models import AbstractUser
class CustomUser(AbstractUser):
phone = models.CharField(max_length=11, verbose_name='Phone Number', unique=True)
class Meta:
db_table = 'custom_user_table'
verbose_name_plural = 'User Records'
def __str__(self):
return self.username
Admin Registration with Encyrpted Password Storage
# api/admin.py
from django.contrib import admin
from .models import CustomUser
from django.contrib.auth.admin import UserAdmin
class CustomUserAdmin(UserAdmin):
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('username', 'password1', 'password2', 'is_staff', 'phone'),
}),
)
list_display = ('username', 'email', 'phone', 'is_staff')
admin.site.register(CustomUser, CustomUserAdmin)
DRF Authentication System
Authentication Flow Implementation
# rest_framework/views.py
class APIView(View):
authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
def get_authenticators(self):
return [auth() for auth in self.authentication_classes]
def perform_authentication(self, request):
request.user
def initialize_request(self, request, *args, **kwargs):
return Request(
request,
authenticators=self.get_authenticators(),
)
def initial(self, request, *args, **kwargs):
self.perform_authentication(request)
# rest_framework/request.py
class Request:
def __init__(self, request, authenticators=None):
self.authenticators = authenticators or ()
@property
def user(self):
if not hasattr(self, '_user'):
self._authenticate()
return self._user
def _authenticate(self):
for auth in self.authenticators:
try:
user_auth_data = auth.authenticate(self)
except exceptions.APIException:
self._not_authenticated()
raise
if user_auth_data is not None:
self._authenticator = auth
self.user, self.auth = user_auth_data
return
self._not_authenticated()
DRF Configuration Settings
# rest_framework/settings.py
DEFAULTS = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication'
],
}
class APISettings:
def __init__(self, user_settings=None, defaults=None):
self.defaults = defaults or DEFAULTS
@property
def user_settings(self):
if not hasattr(self, '_user_settings'):
self._user_settings = getattr(settings, 'REST_FRAMEWORK', {})
return self._user_settings
def __getattr__(self, attr):
if attr not in self.defaults:
raise AttributeError(f"Invalid setting: '{attr}'")
try:
return self.user_settings[attr]
except KeyError:
return self.defaults[attr]
api_settings = APISettings(None, DEFAULTS)
Built-in Authentication Classes
Basic Authentication Implementation
# rest_framework/authentication.py
import base64
from django.contrib.auth import authenticate
class BasicAuthentication(BaseAuthentication):
def authenticate(self, request):
auth_header = request.META.get('HTTP_AUTHORIZATION', b'')
if isinstance(auth_header, str):
auth_header = auth_header.encode('utf-8')
auth_parts = auth_header.split()
if not auth_parts or auth_parts[0].lower() != b'basic':
return None
if len(auth_parts) != 2:
raise exceptions.AuthenticationFailed('Invalid authorization header')
try:
decoded = base64.b64decode(auth_parts[1]).decode('utf-8')
username, password = decoded.split(':', 1)
except (ValueError, UnicodeDecodeError):
raise exceptions.AuthenticationFailed('Invalid credentials encoding')
return self.verify_credentials(username, password, request)
def verify_credentials(self, username, password, request):
user = authenticate(
request=request,
username=username,
password=password
)
if user is None:
raise exceptions.AuthenticationFailed('Invalid credentials')
if not user.is_active:
raise exceptions.AuthenticationFailed('User account disabled')
return (user, None)
JWT Authentication Implementation
Token Generation Process
# rest_framework_jwt/views.py
class JSONWebTokenAPIView(APIView):
permission_classes = ()
authentication_classes = ()
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
if serializer.is_valid():
user = serializer.validated_data.get('user')
token = serializer.validated_data.get('token')
response_data = {'token': token}
return Response(response_data)
return Response(serializer.errors, status=400)
# rest_framework_jwt/serializers.py
class JSONWebTokenSerializer(serializers.Serializer):
username = serializers.CharField()
password = serializers.CharField()
def validate(self, attrs):
credentials = {
'username': attrs.get('username'),
'password': attrs.get('password')
}
user = authenticate(**credentials)
if user:
payload = jwt_payload_handler(user)
return {
'token': jwt_encode_handler(payload),
'user': user
}
raise serializers.ValidationError('Invalid credentials')
Multi-Method Login Implementation
# api/views.py
from rest_framework_jwt.views import JSONWebTokenAPIView
class MultiAuthLoginView(JSONWebTokenAPIView):
serializer_class = CustomAuthSerializer
# api/serializers.py
import re
from .models import CustomUser
class CustomAuthSerializer(serializers.Serializer):
login_field = serializers.CharField()
password = serializers.CharField()
def validate(self, data):
user = self.authenticate_user(data)
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
return {'token': token, 'user': user}
def authenticate_user(self, data):
login_value = data['login_field']
password = data['password']
if '@' in login_value:
user = CustomUser.objects.filter(email=login_value).first()
elif login_value.isdigit() and len(login_value) == 11:
user = CustomUser.objects.filter(phone=login_value).first()
else:
user = CustomUser.objects.filter(username=login_value).first()
if user and user.check_password(password):
return user
raise serializers.ValidationError('Authentication failed')
JWT Configuration Settings
# settings.py
import datetime
JWT_AUTH = {
'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=300),
'JWT_ALLOW_REFRESH': True,
'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=7),
}
Global JWT Configuration
# settings.py
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework_jwt.authentication.JSONWebTokenAuthentication'
],
}