Skip to content

Latest commit

 

History

History
472 lines (375 loc) · 15.1 KB

File metadata and controls

472 lines (375 loc) · 15.1 KB

Exploring Django's Authentication System

Slide 1: Django Authentication System Overview

Django's authentication system is a robust framework that handles user accounts, groups, permissions, and cookie-based user sessions. It seamlessly integrates with Django's built-in views and forms, providing a secure foundation for user management in web applications.

# settings.py
INSTALLED_APPS = [
    'django.contrib.auth',  # Authentication framework
    'django.contrib.contenttypes',  # Required for auth
]

MIDDLEWARE = [
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
]

# Default authentication backends
AUTHENTICATION_BACKENDS = [
    'django.contrib.auth.backends.ModelBackend',
]

Slide 2: Basic User Model Implementation

Django provides a flexible User model that can be extended or customized. The default User model includes essential fields like username, password, email, and permissions, making it suitable for most web applications.

# models.py
from django.contrib.auth.models import AbstractUser
from django.db import models

class CustomUser(AbstractUser):
    date_of_birth = models.DateField(null=True, blank=True)
    phone_number = models.CharField(max_length=15, blank=True)
    
    # Custom property example
    @property
    def full_name(self):
        return f"{self.first_name} {self.last_name}"
    
    def __str__(self):
        return self.username

Slide 3: Authentication Views Setup

The core of Django's authentication system lies in its views. These handle user login, logout, and session management. Here's a comprehensive setup of authentication views with proper security measures.

# views.py
from django.contrib.auth import authenticate, login, logout
from django.shortcuts import render, redirect
from django.contrib.auth.decorators import login_required
from django.contrib import messages

@login_required
def index(request):
    return render(request, 'index.html', {'user': request.user})

def login_view(request):
    if request.method == 'POST':
        username = request.POST['username']
        password = request.POST['password']
        user = authenticate(request, username=username, password=password)
        
        if user is not None:
            login(request, user)
            return redirect('index')
        else:
            messages.error(request, 'Invalid credentials')
    
    return render(request, 'login.html')

def logout_view(request):
    logout(request)
    messages.success(request, 'Successfully logged out')
    return redirect('login')

Slide 4: URL Configuration for Authentication

The URL configuration maps authentication views to specific URLs, creating a coherent flow for user authentication processes. This setup includes both function-based and class-based views.

# urls.py
from django.urls import path
from . import views
from django.contrib.auth import views as auth_views

urlpatterns = [
    path('', views.index, name='index'),
    path('login/', views.login_view, name='login'),
    path('logout/', views.logout_view, name='logout'),
    path('password_reset/', auth_views.PasswordResetView.as_view(), 
         name='password_reset'),
    path('password_reset/done/', 
         auth_views.PasswordResetDoneView.as_view(), 
         name='password_reset_done'),
]

Slide 5: Login Template Implementation

Creating a secure and user-friendly login template is crucial for authentication. This implementation includes CSRF protection, form validation, and error messaging for enhanced security.

# templates/login.html
{% extends 'base.html' %}

{% block content %}
<form method="post" class="login-form">
    {% csrf_token %}
    
    {% if messages %}
    <div class="messages">
        {% for message in messages %}
        <div class="{{ message.tags }}">
            {{ message }}
        </div>
        {% endfor %}
    </div>
    {% endif %}
    
    <div class="form-group">
        <label for="username">Username:</label>
        <input type="text" name="username" required>
    </div>
    
    <div class="form-group">
        <label for="password">Password:</label>
        <input type="password" name="password" required>
    </div>
    
    <button type="submit">Login</button>
</form>
{% endblock %}

Slide 6: User Registration Implementation

A secure user registration system is essential for any web application. This implementation includes form validation, password hashing, and email verification capabilities while following Django's security best practices.

# forms.py
from django import forms
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User

class CustomUserRegistrationForm(UserCreationForm):
    email = forms.EmailField(required=True)
    first_name = forms.CharField(max_length=30, required=True)
    last_name = forms.CharField(max_length=30, required=True)

    class Meta:
        model = User
        fields = ['username', 'email', 'first_name', 'last_name', 
                 'password1', 'password2']

    def save(self, commit=True):
        user = super().save(commit=False)
        user.email = self.cleaned_data['email']
        user.first_name = self.cleaned_data['first_name']
        user.last_name = self.cleaned_data['last_name']
        if commit:
            user.save()
        return user

Slide 7: Registration View Implementation

The registration view handles user creation, validation, and initial setup. This implementation includes proper error handling and success messaging for a smooth user experience.

# views.py
from django.views.generic import CreateView
from django.contrib.auth import login
from django.shortcuts import render, redirect
from .forms import CustomUserRegistrationForm

def register_view(request):
    if request.method == 'POST':
        form = CustomUserRegistrationForm(request.POST)
        if form.is_valid():
            user = form.save()
            login(request, user)
            messages.success(request, 'Registration successful!')
            return redirect('index')
    else:
        form = CustomUserRegistrationForm()
    
    return render(request, 'registration/register.html', 
                 {'form': form})

Slide 8: Password Management System

Django's password management system includes robust features for password reset, change, and validation. This implementation showcases the complete password management workflow.

# settings.py
AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
        'OPTIONS': {
            'min_length': 9,
        }
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]

# Custom password reset configuration
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_USER = 'your-email@gmail.com'
EMAIL_HOST_PASSWORD = 'your-app-specific-password'

Slide 9: Custom Authentication Backend

Creating a custom authentication backend allows for flexible authentication methods beyond the traditional username/password combination, such as email-based authentication or social media integration.

# backends.py
from django.contrib.auth.backends import ModelBackend
from django.contrib.auth import get_user_model
from django.db.models import Q

class EmailOrUsernameModelBackend(ModelBackend):
    def authenticate(self, request, username=None, password=None, **kwargs):
        UserModel = get_user_model()
        
        try:
            # Allow authentication with either username or email
            user = UserModel.objects.get(
                Q(username__iexact=username) | 
                Q(email__iexact=username)
            )
            
            if user.check_password(password):
                return user
                
        except UserModel.DoesNotExist:
            return None
        
    def get_user(self, user_id):
        UserModel = get_user_model()
        try:
            return UserModel.objects.get(pk=user_id)
        except UserModel.DoesNotExist:
            return None

Slide 10: Session Management

Django's session management system provides secure user session handling with configurable settings for session duration, cookie security, and session storage options.

# settings.py
# Session configuration
SESSION_COOKIE_AGE = 1209600  # 2 weeks in seconds
SESSION_EXPIRE_AT_BROWSER_CLOSE = False
SESSION_COOKIE_SECURE = True  # Only send over HTTPS
SESSION_COOKIE_HTTPONLY = True  # Prevent JavaScript access
SESSION_COOKIE_SAMESITE = 'Lax'

# Custom middleware for session handling
from django.contrib.sessions.middleware import SessionMiddleware

class CustomSessionMiddleware(SessionMiddleware):
    def process_request(self, request):
        session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME)
        request.session = self.SessionStore(session_key)
        
    def process_response(self, request, response):
        try:
            modified = request.session.modified
            empty = request.session.is_empty()
        except AttributeError:
            return response
            
        if empty:
            response.delete_cookie(settings.SESSION_COOKIE_NAME)
        else:
            if modified or settings.SESSION_SAVE_EVERY_REQUEST:
                request.session.save()
        return response

Slide 11: Permissions and Groups Management

Django's permission system provides granular control over user access rights. This implementation demonstrates how to create custom permissions, manage user groups, and implement role-based access control.

# models.py
from django.contrib.auth.models import Group, Permission
from django.contrib.contenttypes.models import ContentType

class Article(models.Model):
    title = models.CharField(max_length=200)
    content = models.TextField()
    
    class Meta:
        permissions = [
            ("can_publish_article", "Can publish articles"),
            ("can_edit_others_articles", "Can edit others articles"),
        ]

# Custom permission setup
def create_custom_permissions():
    content_type = ContentType.objects.get_for_model(Article)
    Permission.objects.create(
        codename='can_archive_article',
        name='Can archive articles',
        content_type=content_type,
    )
    
    # Create editor group with permissions
    editor_group, created = Group.objects.get_or_create(name='Editors')
    permissions = Permission.objects.filter(
        content_type=content_type,
        codename__in=['can_publish_article', 'can_edit_others_articles']
    )
    editor_group.permissions.add(*permissions)

Slide 12: Decorators for Access Control

Custom decorators provide a clean way to implement access control across views. This implementation shows how to create and use permission-based decorators.

# decorators.py
from django.core.exceptions import PermissionDenied
from django.contrib.auth.decorators import user_passes_test
from functools import wraps

def group_required(group_name):
    def decorator(view_func):
        @wraps(view_func)
        def wrapper(request, *args, **kwargs):
            if request.user.groups.filter(name=group_name).exists():
                return view_func(request, *args, **kwargs)
            raise PermissionDenied
        return wrapper
    return decorator

def multiple_permissions_required(permission_list):
    def decorator(view_func):
        @wraps(view_func)
        def wrapper(request, *args, **kwargs):
            if request.user.has_perms(permission_list):
                return view_func(request, *args, **kwargs)
            raise PermissionDenied
        return wrapper
    return decorator

# Usage example
@group_required('Editors')
@multiple_permissions_required(['myapp.can_publish_article'])
def publish_article(request, article_id):
    # View logic here
    pass

Slide 13: Token Authentication Implementation

Token-based authentication is crucial for API security. This implementation shows how to set up token authentication with custom token generation and validation.

# models.py
from django.conf import settings
from django.db import models
import binascii
import os

class Token(models.Model):
    user = models.OneToOneField(
        settings.AUTH_USER_MODEL, 
        on_delete=models.CASCADE,
        related_name='auth_token'
    )
    key = models.CharField(max_length=40, unique=True)
    created = models.DateTimeField(auto_now_add=True)

    def save(self, *args, **kwargs):
        if not self.key:
            self.key = self.generate_key()
        return super().save(*args, **kwargs)

    def generate_key(self):
        return binascii.hexlify(os.urandom(20)).decode()

    def __str__(self):
        return self.key

# Authentication backend for token auth
from django.contrib.auth.backends import BaseBackend

class TokenBackend(BaseBackend):
    def authenticate(self, request, token=None):
        try:
            token_obj = Token.objects.get(key=token)
            return token_obj.user
        except Token.DoesNotExist:
            return None

Slide 14: Security Middleware Implementation

Custom security middleware adds extra layers of protection to Django's authentication system. This implementation includes request validation and security headers.

# middleware.py
from django.conf import settings
from django.http import HttpResponseForbidden
import re

class SecurityMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        # Check for suspicious patterns
        if self.is_suspicious_request(request):
            return HttpResponseForbidden()

        response = self.get_response(request)
        
        # Add security headers
        response['X-Content-Type-Options'] = 'nosniff'
        response['X-Frame-Options'] = 'DENY'
        response['Strict-Transport-Security'] = 'max-age=31536000'
        
        return response

    def is_suspicious_request(self, request):
        suspicious_patterns = [
            r'(?i)(<script|alert\(|eval\()',
            r'(?i)(union.*select)',
            r'(?i)(../../)',
        ]
        
        for pattern in suspicious_patterns:
            if re.search(pattern, request.path):
                return True
            
            if request.GET and any(
                re.search(pattern, v) 
                for v in request.GET.values()
            ):
                return True
        
        return False

Slide 15: Additional Resources