Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Custom User Model Authentication in Django REST Framework

Tech May 15 1

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'
    ],
}

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.