Skip to main content

Authentication & Authorization

Django provides a built-in authentication system for handling users, groups, and permissions.

User Authentication

Login and Logout Views

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

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('dashboard')
else:
messages.error(request, 'Invalid credentials')

return render(request, 'registration/login.html')

def logout_view(request):
logout(request)
return redirect('home')

@login_required
def dashboard(request):
return render(request, 'dashboard.html')

Built-in Authentication Views

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

urlpatterns = [
path('login/', auth_views.LoginView.as_view(), name='login'),
path('logout/', auth_views.LogoutView.as_view(), name='logout'),
path('password_change/', auth_views.PasswordChangeView.as_view(), name='password_change'),
path('password_reset/', auth_views.PasswordResetView.as_view(), name='password_reset'),
]

User Registration

Registration Form

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

class CustomUserCreationForm(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', 'first_name', 'last_name', 'email', '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

Registration View

def register(request):
if request.method == 'POST':
form = CustomUserCreationForm(request.POST)
if form.is_valid():
user = form.save()
username = form.cleaned_data.get('username')
messages.success(request, f'Account created for {username}!')
return redirect('login')
else:
form = CustomUserCreationForm()
return render(request, 'registration/register.html', {'form': form})

Custom User Model

Extended User Model

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

class CustomUser(AbstractUser):
email = models.EmailField(unique=True)
birth_date = models.DateField(null=True, blank=True)
profile_picture = models.ImageField(upload_to='profiles/', null=True, blank=True)
bio = models.TextField(max_length=500, blank=True)

USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
# settings.py
AUTH_USER_MODEL = 'myapp.CustomUser'

User Profile Model

from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver

class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
bio = models.TextField(max_length=500, blank=True)
location = models.CharField(max_length=30, blank=True)
birth_date = models.DateField(null=True, blank=True)
avatar = models.ImageField(upload_to='avatars/', null=True, blank=True)

@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)

@receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()

Permissions and Groups

Creating Permissions

# models.py
class Article(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
author = models.ForeignKey(User, on_delete=models.CASCADE)

class Meta:
permissions = [
('can_publish', 'Can publish articles'),
('can_edit_all', 'Can edit all articles'),
]

Checking Permissions

# In views
from django.contrib.auth.decorators import permission_required
from django.contrib.auth.mixins import PermissionRequiredMixin

@permission_required('myapp.can_publish')
def publish_article(request, article_id):
article = get_object_or_404(Article, id=article_id)
article.is_published = True
article.save()
return redirect('article_detail', pk=article.pk)

class PublishArticleView(PermissionRequiredMixin, View):
permission_required = 'myapp.can_publish'

def post(self, request, pk):
article = get_object_or_404(Article, pk=pk)
article.is_published = True
article.save()
return redirect('article_detail', pk=article.pk)

Working with Groups

from django.contrib.auth.models import Group, Permission

# Create groups
editors_group = Group.objects.create(name='Editors')
authors_group = Group.objects.create(name='Authors')

# Add permissions to groups
publish_permission = Permission.objects.get(codename='can_publish')
editors_group.permissions.add(publish_permission)

# Add users to groups
user.groups.add(editors_group)

# Check group membership
if user.groups.filter(name='Editors').exists():
# User is an editor
pass

Login Required and Access Control

Function-Based Views

from django.contrib.auth.decorators import login_required, user_passes_test

@login_required
def profile_view(request):
return render(request, 'profile.html')

@login_required(login_url='/accounts/login/')
def dashboard(request):
return render(request, 'dashboard.html')

def is_editor(user):
return user.groups.filter(name='Editors').exists()

@user_passes_test(is_editor)
def editor_panel(request):
return render(request, 'editor_panel.html')

Class-Based Views

from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin

class ProfileView(LoginRequiredMixin, TemplateView):
template_name = 'profile.html'
login_url = '/accounts/login/'

class EditorPanelView(UserPassesTestMixin, TemplateView):
template_name = 'editor_panel.html'

def test_func(self):
return self.request.user.groups.filter(name='Editors').exists()

class ArticleUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model = Article
fields = ['title', 'content']

def test_func(self):
article = self.get_object()
return self.request.user == article.author

Session Management

Working with Sessions

def session_example(request):
# Set session data
request.session['user_preference'] = 'dark_mode'
request.session['cart_items'] = ['item1', 'item2']

# Get session data
preference = request.session.get('user_preference', 'light_mode')
cart = request.session.get('cart_items', [])

# Delete session data
if 'old_data' in request.session:
del request.session['old_data']

# Clear all session data
request.session.flush()

return render(request, 'template.html', {
'preference': preference,
'cart': cart
})

Password Management

Password Validation

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

Custom Password Change

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) # Keep user logged in
messages.success(request, 'Password changed successfully!')
return redirect('profile')
else:
form = PasswordChangeForm(request.user)
return render(request, 'change_password.html', {'form': form})

Security Best Practices

Secure Settings

# settings.py

# Session security
SESSION_COOKIE_SECURE = True # Only send over HTTPS
SESSION_COOKIE_HTTPONLY = True # No JavaScript access
SESSION_COOKIE_AGE = 3600 # 1 hour

# CSRF protection
CSRF_COOKIE_SECURE = True
CSRF_COOKIE_HTTPONLY = True

# Login/logout URLs
LOGIN_URL = '/accounts/login/'
LOGIN_REDIRECT_URL = '/dashboard/'
LOGOUT_REDIRECT_URL = '/'

# Account lockout
ACCOUNT_LOCKOUT_TIME = 300 # 5 minutes
ACCOUNT_LOCKOUT_THRESHOLD = 5 # attempts

Rate Limiting

# Using django-ratelimit
from django_ratelimit.decorators import ratelimit

@ratelimit(key='ip', rate='5/m', method='POST')
def login_view(request):
# Login logic
pass

Authentication Templates

Login Template

<!-- registration/login.html -->
{% extends 'base.html' %} {% block title %}Login{% endblock %} {% block content
%}
<h2>Login</h2>

{% if messages %} {% for message in messages %}
<div class="alert alert-{{ message.tags }}">{{ message }}</div>
{% endfor %} {% endif %}

<form method="post">
{% csrf_token %} {{ form.as_p }}
<button type="submit" class="btn btn-primary">Login</button>
</form>

<p><a href="{% url 'password_reset' %}">Forgot password?</a></p>
<p><a href="{% url 'register' %}">Don't have an account? Register</a></p>
{% endblock %}