Authentication
Essential authentication, authorization, and security features for Django applications.
User Authentication
Built-in User Model
from django.contrib.auth.models import User
from django.contrib.auth import authenticate, login, logout
# Create user
user = User.objects.create_user(
username='john',
email='john@example.com',
password='password123'
)
# Authenticate user
user = authenticate(username='john', password='password123')
if user:
login(request, user)
Custom User Model
# models.py
from django.contrib.auth.models import AbstractUser
class CustomUser(AbstractUser):
email = models.EmailField(unique=True)
phone = models.CharField(max_length=15, blank=True)
birth_date = models.DateField(null=True, blank=True)
# settings.py
AUTH_USER_MODEL = 'myapp.CustomUser'
Login/Logout Views
from django.contrib.auth import login, authenticate, logout
from django.contrib.auth.forms import UserCreationForm
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:
login(request, user)
return redirect('dashboard')
return render(request, 'login.html')
def logout_view(request):
logout(request)
return redirect('home')
def register_view(request):
if request.method == 'POST':
form = UserCreationForm(request.POST)
if form.is_valid():
user = form.save()
login(request, user)
return redirect('dashboard')
else:
form = UserCreationForm()
return render(request, 'register.html', {'form': form})
Authorization & Permissions
View Decorators
from django.contrib.auth.decorators import login_required, permission_required
from django.contrib.admin.views.decorators import staff_member_required
@login_required
def protected_view(request):
return render(request, 'protected.html')
@permission_required('myapp.add_post')
def create_post(request):
# Only users with add_post permission can access
pass
@staff_member_required
def admin_only(request):
# Only staff members can access
pass
Class-Based View Mixins
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
class ProtectedListView(LoginRequiredMixin, ListView):
model = MyModel
login_url = '/login/'
class CreatePostView(PermissionRequiredMixin, CreateView):
model = Post
permission_required = 'myapp.add_post'
fields = ['title', 'content']
Groups & Permissions
from django.contrib.auth.models import Group, Permission
from django.contrib.contenttypes.models import ContentType
# Create group
editors = Group.objects.create(name='Editors')
# Add permission to group
content_type = ContentType.objects.get_for_model(Post)
permission = Permission.objects.create(
codename='can_publish',
name='Can publish posts',
content_type=content_type,
)
editors.permissions.add(permission)
# Add user to group
user.groups.add(editors)
# Check permissions
if user.has_perm('myapp.can_publish'):
# User can publish
pass
Security Features
CSRF Protection
# Enabled by default in settings.py
MIDDLEWARE = [
'django.middleware.csrf.CsrfViewMiddleware',
]
# In templates
{% csrf_token %}
# In AJAX requests
$.ajaxSetup({
beforeSend: function(xhr, settings) {
xhr.setRequestHeader("X-CSRFToken", $('[name=csrfmiddlewaretoken]').val());
}
});
Password Security
# settings.py
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
'OPTIONS': {'min_length': 8,}
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Change password
from django.contrib.auth import update_session_auth_hash
from django.contrib.auth.forms import PasswordChangeForm
def change_password(request):
if request.method == 'POST':
form = PasswordChangeForm(request.user, request.POST)
if form.is_valid():
user = form.save()
update_session_auth_hash(request, user)
return redirect('success')
Session Security
# settings.py
SESSION_COOKIE_SECURE = True # HTTPS only
SESSION_COOKIE_HTTPONLY = True # No JavaScript access
SESSION_COOKIE_AGE = 3600 # 1 hour
SESSION_EXPIRE_AT_BROWSER_CLOSE = True
Security Best Practices
Input Validation
from django.core.exceptions import ValidationError
from django.core.validators import RegexValidator
# Model validation
class Post(models.Model):
title = models.CharField(
max_length=200,
validators=[
RegexValidator(
regex='^[a-zA-Z0-9\s]+$',
message='Title must contain only letters, numbers, and spaces'
)
]
)
# Form validation
class ContactForm(forms.Form):
email = forms.EmailField()
def clean_email(self):
email = self.cleaned_data['email']
if not email.endswith('@example.com'):
raise ValidationError('Must use company email')
return email
SQL Injection Prevention
# Good - Using ORM
users = User.objects.filter(username=username)
# Good - Parameterized queries
from django.db import connection
cursor = connection.cursor()
cursor.execute("SELECT * FROM users WHERE username = %s", [username])
# Bad - String concatenation (DON'T DO THIS)
# cursor.execute(f"SELECT * FROM users WHERE username = '{username}'")
XSS Prevention
# Templates automatically escape by default
{{ user.name }} # Safe
# Manual escaping
from django.utils.html import escape
safe_content = escape(user_input)
# Mark as safe (use carefully)
from django.utils.safestring import mark_safe
content = mark_safe('<strong>Safe HTML</strong>')
Advanced Authentication
Custom Authentication Backend
from django.contrib.auth.backends import BaseBackend
from django.contrib.auth.models import User
class EmailBackend(BaseBackend):
def authenticate(self, request, username=None, password=None, **kwargs):
try:
user = User.objects.get(email=username)
if user.check_password(password):
return user
except User.DoesNotExist:
return None
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
# settings.py
AUTHENTICATION_BACKENDS = [
'myapp.backends.EmailBackend',
'django.contrib.auth.backends.ModelBackend',
]
Two-Factor Authentication
# Using django-otp
INSTALLED_APPS = [
'django_otp',
'django_otp.plugins.otp_totp',
'django_otp.plugins.otp_static',
]
MIDDLEWARE = [
'django_otp.middleware.OTPMiddleware',
]
# View protection
from django_otp.decorators import otp_required
@otp_required
def sensitive_view(request):
return render(request, 'sensitive.html')
Rate Limiting
# Using django-ratelimit
from django_ratelimit.decorators import ratelimit
@ratelimit(key='ip', rate='5/m', method='POST')
def login_view(request):
# Limited to 5 POST requests per minute per IP
pass
Production Security Settings
Essential Security Settings
# settings.py - Production security settings
DEBUG = False
ALLOWED_HOSTS = ['yourdomain.com']
# SSL/HTTPS
SECURE_SSL_REDIRECT = True
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
# HSTS
SECURE_HSTS_SECONDS = 31536000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
# Content security
SECURE_CONTENT_TYPE_NOSNIFF = True
SECURE_BROWSER_XSS_FILTER = True
X_FRAME_OPTIONS = 'DENY'
# Secret key
SECRET_KEY = os.environ.get('SECRET_KEY')
Security Monitoring
import logging
security_logger = logging.getLogger('security')
def login_view(request):
user = authenticate(request, username=username, password=password)
if user:
login(request, user)
security_logger.info(f'Successful login for user {username}')
else:
security_logger.warning(f'Failed login attempt for user {username}')