Django provides built-in authentication views for common authentication workflows like login, logout, password change, and password reset. Understanding how to use and customize these views enables you to implement secure authentication flows that meet your application's specific requirements.
# Django's built-in authentication views
from django.contrib.auth.views import (
LoginView, LogoutView, PasswordChangeView, PasswordChangeDoneView,
PasswordResetView, PasswordResetDoneView, PasswordResetConfirmView,
PasswordResetCompleteView
)
from django.urls import path, reverse_lazy
from django.shortcuts import render, redirect
from django.contrib.auth import authenticate, login, logout
from django.contrib import messages
class AuthenticationViewOverview:
"""Overview of Django's built-in authentication views"""
@staticmethod
def built_in_views_summary():
"""Summary of all built-in authentication views"""
views_summary = {
'LoginView': {
'purpose': 'Handle user login',
'template': 'registration/login.html',
'success_url': 'LOGIN_REDIRECT_URL setting',
'form_class': 'AuthenticationForm'
},
'LogoutView': {
'purpose': 'Handle user logout',
'template': 'registration/logged_out.html',
'success_url': 'LOGOUT_REDIRECT_URL setting',
'form_class': None
},
'PasswordChangeView': {
'purpose': 'Change user password',
'template': 'registration/password_change_form.html',
'success_url': 'password_change_done',
'form_class': 'PasswordChangeForm'
},
'PasswordChangeDoneView': {
'purpose': 'Password change success page',
'template': 'registration/password_change_done.html',
'success_url': None,
'form_class': None
},
'PasswordResetView': {
'purpose': 'Request password reset',
'template': 'registration/password_reset_form.html',
'success_url': 'password_reset_done',
'form_class': 'PasswordResetForm'
},
'PasswordResetDoneView': {
'purpose': 'Password reset request confirmation',
'template': 'registration/password_reset_done.html',
'success_url': None,
'form_class': None
},
'PasswordResetConfirmView': {
'purpose': 'Confirm password reset with token',
'template': 'registration/password_reset_confirm.html',
'success_url': 'password_reset_complete',
'form_class': 'SetPasswordForm'
},
'PasswordResetCompleteView': {
'purpose': 'Password reset completion page',
'template': 'registration/password_reset_complete.html',
'success_url': None,
'form_class': None
}
}
return views_summary
@staticmethod
def basic_url_configuration():
"""Basic URL configuration for authentication views"""
# Method 1: Use Django's built-in auth URLs
urlpatterns_builtin = [
path('accounts/', include('django.contrib.auth.urls')),
]
# This includes all these URLs:
# accounts/login/ [name='login']
# accounts/logout/ [name='logout']
# accounts/password_change/ [name='password_change']
# accounts/password_change/done/ [name='password_change_done']
# accounts/password_reset/ [name='password_reset']
# accounts/password_reset/done/ [name='password_reset_done']
# accounts/reset/<uidb64>/<token>/ [name='password_reset_confirm']
# accounts/reset/done/ [name='password_reset_complete']
# Method 2: Define individual views
urlpatterns_individual = [
path('login/', LoginView.as_view(), name='login'),
path('logout/', LogoutView.as_view(), name='logout'),
path('password_change/', PasswordChangeView.as_view(), name='password_change'),
path('password_change/done/', PasswordChangeDoneView.as_view(), name='password_change_done'),
path('password_reset/', PasswordResetView.as_view(), name='password_reset'),
path('password_reset/done/', PasswordResetDoneView.as_view(), name='password_reset_done'),
path('reset/<uidb64>/<token>/', PasswordResetConfirmView.as_view(), name='password_reset_confirm'),
path('reset/done/', PasswordResetCompleteView.as_view(), name='password_reset_complete'),
]
return urlpatterns_builtin, urlpatterns_individual
Let me continue with the rest of the content:
<function_calls>
# Custom login view implementations
from django.contrib.auth.views import LoginView
from django.contrib.auth.forms import AuthenticationForm
from django.contrib.auth import login
from django.shortcuts import render, redirect
from django.contrib import messages
from django.urls import reverse_lazy
from django.utils.decorators import method_decorator
from django.views.decorators.cache import never_cache
from django.views.decorators.csrf import csrf_protect
from django.views.decorators.debug import sensitive_post_parameters
class CustomLoginView(LoginView):
"""Enhanced login view with additional features"""
template_name = 'auth/login.html'
form_class = AuthenticationForm
redirect_authenticated_user = True
@method_decorator(sensitive_post_parameters())
@method_decorator(csrf_protect)
@method_decorator(never_cache)
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request, *args, **kwargs)
def get_success_url(self):
"""Determine where to redirect after successful login"""
# Check for 'next' parameter
next_url = self.request.POST.get('next') or self.request.GET.get('next')
if next_url:
return next_url
# Redirect based on user type
user = self.request.user
if user.is_superuser:
return reverse_lazy('admin:index')
elif user.is_staff:
return reverse_lazy('staff_dashboard')
elif user.groups.filter(name='Editors').exists():
return reverse_lazy('editor_dashboard')
else:
return reverse_lazy('user_dashboard')
def form_valid(self, form):
"""Handle successful login"""
response = super().form_valid(form)
user = form.get_user()
# Log successful login
import logging
logger = logging.getLogger('auth')
logger.info(f"User {user.username} logged in from {self.request.META.get('REMOTE_ADDR')}")
# Set session data
self.request.session['login_timestamp'] = timezone.now().isoformat()
self.request.session['user_agent'] = self.request.META.get('HTTP_USER_AGENT', '')[:200]
# Handle "Remember Me" functionality
remember_me = self.request.POST.get('remember_me')
if remember_me:
# Set session to expire when browser closes (default)
self.request.session.set_expiry(0)
else:
# Set session to expire in 1 hour
self.request.session.set_expiry(3600)
# Add success message
messages.success(
self.request,
f'Welcome back, {user.get_full_name() or user.username}!'
)
# Update last login timestamp
user.last_login = timezone.now()
user.save(update_fields=['last_login'])
return response
def form_invalid(self, form):
"""Handle failed login attempt"""
response = super().form_invalid(form)
# Log failed login attempt
username = form.cleaned_data.get('username', 'unknown')
import logging
logger = logging.getLogger('auth')
logger.warning(
f"Failed login attempt for username: {username} "
f"from {self.request.META.get('REMOTE_ADDR')}"
)
# Implement rate limiting (basic example)
from django.core.cache import cache
ip_address = self.request.META.get('REMOTE_ADDR')
cache_key = f"failed_login_{ip_address}"
failed_attempts = cache.get(cache_key, 0) + 1
cache.set(cache_key, failed_attempts, 300) # 5 minutes
if failed_attempts >= 5:
messages.error(
self.request,
"Too many failed login attempts. Please try again in 5 minutes."
)
return response
def get_context_data(self, **kwargs):
"""Add additional context to template"""
context = super().get_context_data(**kwargs)
context.update({
'site_name': 'Your Site Name',
'allow_registration': True,
'social_login_enabled': True,
'password_reset_enabled': True,
})
return context
class AjaxLoginView(LoginView):
"""Login view that supports AJAX requests"""
def form_valid(self, form):
"""Handle successful AJAX login"""
if self.request.headers.get('X-Requested-With') == 'XMLHttpRequest':
login(self.request, form.get_user())
return JsonResponse({
'success': True,
'message': 'Login successful',
'redirect_url': self.get_success_url()
})
return super().form_valid(form)
def form_invalid(self, form):
"""Handle failed AJAX login"""
if self.request.headers.get('X-Requested-With') == 'XMLHttpRequest':
return JsonResponse({
'success': False,
'errors': form.errors,
'message': 'Login failed'
})
return super().form_invalid(form)
# Function-based login view
def custom_login_view(request):
"""Custom function-based login view"""
if request.user.is_authenticated:
return redirect('dashboard')
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
remember_me = request.POST.get('remember_me')
# Authenticate user
user = authenticate(request, username=username, password=password)
if user is not None:
if user.is_active:
# Log in user
login(request, user)
# Handle remember me
if not remember_me:
request.session.set_expiry(0) # Browser session
# Add success message
messages.success(request, f'Welcome, {user.get_full_name() or user.username}!')
# Redirect to next or default
next_url = request.POST.get('next') or request.GET.get('next')
return redirect(next_url or 'dashboard')
else:
messages.error(request, 'Your account is disabled.')
else:
messages.error(request, 'Invalid username or password.')
context = {
'next': request.GET.get('next', ''),
}
return render(request, 'auth/login.html', context)
# Custom logout view implementations
from django.contrib.auth.views import LogoutView
from django.contrib.auth import logout
from django.shortcuts import render, redirect
class CustomLogoutView(LogoutView):
"""Enhanced logout view with additional features"""
template_name = 'auth/logout.html'
def dispatch(self, request, *args, **kwargs):
"""Handle logout process"""
if request.user.is_authenticated:
# Log logout
import logging
logger = logging.getLogger('auth')
logger.info(f"User {request.user.username} logged out")
# Store user info before logout
user_info = {
'username': request.user.username,
'full_name': request.user.get_full_name(),
}
# Clear custom session data
session_keys_to_clear = [
'user_preferences', 'shopping_cart', 'temp_data'
]
for key in session_keys_to_clear:
request.session.pop(key, None)
# Add logout message
messages.info(
request,
f"You have been logged out, {user_info['full_name'] or user_info['username']}."
)
return super().dispatch(request, *args, **kwargs)
def get_next_page(self):
"""Determine where to redirect after logout"""
next_page = super().get_next_page()
if next_page:
return next_page
# Custom logout redirect logic
return reverse_lazy('home')
class AjaxLogoutView(LogoutView):
"""Logout view that supports AJAX requests"""
def dispatch(self, request, *args, **kwargs):
"""Handle AJAX logout"""
if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
if request.user.is_authenticated:
username = request.user.username
logout(request)
return JsonResponse({
'success': True,
'message': f'Goodbye, {username}!',
'redirect_url': self.get_next_page()
})
else:
return JsonResponse({
'success': False,
'message': 'Not logged in'
})
return super().dispatch(request, *args, **kwargs)
# Function-based logout view
def custom_logout_view(request):
"""Custom function-based logout view"""
if request.user.is_authenticated:
username = request.user.username
# Clear specific session data
request.session.pop('user_preferences', None)
# Log out user
logout(request)
messages.success(request, f'You have been logged out, {username}.')
return redirect('home')
# Custom password change views
from django.contrib.auth.views import PasswordChangeView, PasswordChangeDoneView
from django.contrib.auth.forms import PasswordChangeForm
from django.contrib.auth.decorators import login_required
class CustomPasswordChangeView(PasswordChangeView):
"""Enhanced password change view"""
template_name = 'auth/password_change.html'
success_url = reverse_lazy('password_change_done')
def form_valid(self, form):
"""Handle successful password change"""
response = super().form_valid(form)
user = self.request.user
# Log password change
import logging
logger = logging.getLogger('auth')
logger.info(f"Password changed for user: {user.username}")
# Store password change timestamp
if hasattr(user, 'profile'):
user.profile.password_changed_at = timezone.now()
user.profile.save()
# Invalidate all other sessions
from django.contrib.sessions.models import Session
current_session_key = self.request.session.session_key
# Get all user sessions
user_sessions = []
for session in Session.objects.all():
try:
session_data = session.get_decoded()
if session_data.get('_auth_user_id') == str(user.id):
if session.session_key != current_session_key:
user_sessions.append(session)
except:
continue
# Delete other sessions
for session in user_sessions:
session.delete()
messages.success(
self.request,
'Your password has been changed successfully. '
'All other sessions have been logged out for security.'
)
return response
def get_context_data(self, **kwargs):
"""Add password strength requirements to context"""
context = super().get_context_data(**kwargs)
context.update({
'password_requirements': [
'At least 12 characters long',
'Contains uppercase and lowercase letters',
'Contains at least one number',
'Contains at least one special character',
'Not similar to your personal information',
'Not a commonly used password'
]
})
return context
class CustomPasswordChangeDoneView(PasswordChangeDoneView):
"""Custom password change success view"""
template_name = 'auth/password_change_done.html'
def get_context_data(self, **kwargs):
"""Add additional context"""
context = super().get_context_data(**kwargs)
context.update({
'next_steps': [
'Update your password in any saved browsers',
'Update your password in mobile apps',
'Consider enabling two-factor authentication'
]
})
return context
# Function-based password change view
@login_required
def custom_password_change_view(request):
"""Custom function-based password change view"""
if request.method == 'POST':
form = PasswordChangeForm(request.user, request.POST)
if form.is_valid():
user = form.save()
# Update session to prevent logout
from django.contrib.auth import update_session_auth_hash
update_session_auth_hash(request, user)
messages.success(request, 'Your password has been changed successfully!')
return redirect('password_change_done')
else:
form = PasswordChangeForm(request.user)
return render(request, 'auth/password_change.html', {'form': form})
# Custom password reset views
from django.contrib.auth.views import (
PasswordResetView, PasswordResetDoneView,
PasswordResetConfirmView, PasswordResetCompleteView
)
from django.contrib.auth.forms import PasswordResetForm, SetPasswordForm
from django.core.mail import send_mail
from django.template.loader import render_to_string
class CustomPasswordResetView(PasswordResetView):
"""Enhanced password reset view"""
template_name = 'auth/password_reset.html'
email_template_name = 'auth/password_reset_email.html'
subject_template_name = 'auth/password_reset_subject.txt'
success_url = reverse_lazy('password_reset_done')
def form_valid(self, form):
"""Handle password reset request"""
email = form.cleaned_data['email']
# Rate limiting
from django.core.cache import cache
cache_key = f"password_reset_{email}"
recent_requests = cache.get(cache_key, 0)
if recent_requests >= 3:
messages.error(
self.request,
"Too many password reset requests. Please try again later."
)
return redirect('password_reset')
# Increment request count
cache.set(cache_key, recent_requests + 1, 3600) # 1 hour
# Log password reset request
import logging
logger = logging.getLogger('auth')
logger.info(f"Password reset requested for email: {email}")
return super().form_valid(form)
def send_mail(self, subject_template_name, email_template_name,
context, from_email, to_email, html_email_template_name=None):
"""Send password reset email with enhanced template"""
# Add additional context
context.update({
'site_name': 'Your Site Name',
'support_email': 'support@yoursite.com',
'security_tips': [
'Never share your password with anyone',
'Use a unique password for this account',
'Consider enabling two-factor authentication'
]
})
return super().send_mail(
subject_template_name, email_template_name, context,
from_email, to_email, html_email_template_name
)
class CustomPasswordResetConfirmView(PasswordResetConfirmView):
"""Enhanced password reset confirmation view"""
template_name = 'auth/password_reset_confirm.html'
success_url = reverse_lazy('password_reset_complete')
def form_valid(self, form):
"""Handle successful password reset"""
response = super().form_valid(form)
user = form.user
# Log successful password reset
import logging
logger = logging.getLogger('auth')
logger.info(f"Password reset completed for user: {user.username}")
# Invalidate all user sessions
from django.contrib.sessions.models import Session
for session in Session.objects.all():
try:
session_data = session.get_decoded()
if session_data.get('_auth_user_id') == str(user.id):
session.delete()
except:
continue
# Send confirmation email
send_mail(
'Password Reset Successful',
f'Your password has been reset successfully for {user.username}.',
settings.DEFAULT_FROM_EMAIL,
[user.email],
fail_silently=True,
)
messages.success(
self.request,
'Your password has been reset successfully. '
'Please log in with your new password.'
)
return response
# Two-factor authentication integration
class TwoFactorLoginView(LoginView):
"""Login view with two-factor authentication"""
template_name = 'auth/2fa_login.html'
def form_valid(self, form):
"""Handle login with 2FA check"""
user = form.get_user()
# Check if user has 2FA enabled
if hasattr(user, 'profile') and user.profile.two_factor_enabled:
# Store user in session temporarily
self.request.session['2fa_user_id'] = user.id
self.request.session['2fa_timestamp'] = timezone.now().isoformat()
# Redirect to 2FA verification
return redirect('2fa_verify')
# Normal login process
return super().form_valid(form)
def two_factor_verify_view(request):
"""Verify two-factor authentication token"""
if request.method == 'POST':
token = request.POST.get('token')
user_id = request.session.get('2fa_user_id')
if not user_id:
messages.error(request, 'Invalid session. Please log in again.')
return redirect('login')
try:
user = User.objects.get(id=user_id)
# Verify TOTP token (example with pyotp)
import pyotp
if hasattr(user, 'profile') and user.profile.totp_secret:
totp = pyotp.TOTP(user.profile.totp_secret)
if totp.verify(token, valid_window=1):
# Complete login
login(request, user)
# Clean up session
del request.session['2fa_user_id']
del request.session['2fa_timestamp']
messages.success(request, 'Login successful!')
return redirect('dashboard')
else:
messages.error(request, 'Invalid verification code.')
else:
messages.error(request, 'Two-factor authentication not properly configured.')
except User.DoesNotExist:
messages.error(request, 'Invalid session. Please log in again.')
return redirect('login')
return render(request, 'auth/2fa_verify.html')
Django's authentication views provide a solid foundation for implementing secure authentication workflows. By customizing these views, you can add enhanced security features, improved user experience, and integration with additional authentication methods while maintaining the robustness of Django's built-in security measures.
# Custom user registration views
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth import login
from django.views.generic import CreateView
from django.urls import reverse_lazy
class CustomUserCreationForm(UserCreationForm):
"""Enhanced user creation form"""
email = forms.EmailField(required=True)
first_name = forms.CharField(max_length=30, required=True)
last_name = forms.CharField(max_length=30, required=True)
terms_accepted = forms.BooleanField(
required=True,
label="I accept the Terms of Service and Privacy Policy"
)
class Meta:
model = User
fields = ('username', 'first_name', 'last_name', 'email', 'password1', 'password2')
def clean_email(self):
"""Validate email uniqueness"""
email = self.cleaned_data['email']
if User.objects.filter(email=email).exists():
raise forms.ValidationError("A user with this email already exists.")
return email
def clean_username(self):
"""Additional username validation"""
username = self.cleaned_data['username']
# Check for reserved usernames
reserved_usernames = ['admin', 'root', 'administrator', 'moderator']
if username.lower() in reserved_usernames:
raise forms.ValidationError("This username is reserved.")
return username
def save(self, commit=True):
"""Save user with additional fields"""
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
class RegistrationView(CreateView):
"""User registration view"""
form_class = CustomUserCreationForm
template_name = 'auth/register.html'
success_url = reverse_lazy('registration_complete')
def dispatch(self, request, *args, **kwargs):
"""Redirect authenticated users"""
if request.user.is_authenticated:
return redirect('dashboard')
return super().dispatch(request, *args, **kwargs)
def form_valid(self, form):
"""Handle successful registration"""
response = super().form_valid(form)
user = self.object
# Log registration
import logging
logger = logging.getLogger('auth')
logger.info(f"New user registered: {user.username} ({user.email})")
# Send welcome email
self.send_welcome_email(user)
# Auto-login after registration
login(self.request, user)
messages.success(
self.request,
f'Welcome to our site, {user.get_full_name() or user.username}! '
'Your account has been created successfully.'
)
return response
def send_welcome_email(self, user):
"""Send welcome email to new user"""
subject = 'Welcome to Our Site!'
message = render_to_string('auth/welcome_email.html', {
'user': user,
'site_name': 'Your Site Name',
})
send_mail(
subject,
message,
settings.DEFAULT_FROM_EMAIL,
[user.email],
fail_silently=True,
)
class EmailVerificationRequiredRegistrationView(CreateView):
"""Registration view that requires email verification"""
form_class = CustomUserCreationForm
template_name = 'auth/register.html'
success_url = reverse_lazy('registration_email_sent')
def form_valid(self, form):
"""Handle registration with email verification"""
response = super().form_valid(form)
user = self.object
# Set user as inactive until email verification
user.is_active = False
user.save()
# Send verification email
self.send_verification_email(user)
messages.info(
self.request,
'Please check your email and click the verification link to activate your account.'
)
return response
def send_verification_email(self, user):
"""Send email verification link"""
from django.contrib.auth.tokens import default_token_generator
from django.utils.http import urlsafe_base64_encode
from django.utils.encoding import force_bytes
# Generate verification token
token = default_token_generator.make_token(user)
uid = urlsafe_base64_encode(force_bytes(user.pk))
# Create verification URL
verification_url = self.request.build_absolute_uri(
reverse('email_verify', kwargs={'uidb64': uid, 'token': token})
)
# Send email
subject = 'Verify Your Email Address'
message = render_to_string('auth/verification_email.html', {
'user': user,
'verification_url': verification_url,
'site_name': 'Your Site Name',
})
send_mail(
subject,
message,
settings.DEFAULT_FROM_EMAIL,
[user.email],
fail_silently=False,
)
def email_verify_view(request, uidb64, token):
"""Verify email address"""
try:
uid = force_str(urlsafe_base64_decode(uidb64))
user = User.objects.get(pk=uid)
except (TypeError, ValueError, OverflowError, User.DoesNotExist):
user = None
if user is not None and default_token_generator.check_token(user, token):
# Activate user account
user.is_active = True
user.save()
# Log in user
login(request, user)
messages.success(
request,
'Your email has been verified successfully! Welcome to our site.'
)
return redirect('dashboard')
else:
messages.error(
request,
'The verification link is invalid or has expired.'
)
return redirect('register')
# Function-based registration view
def register_view(request):
"""Function-based registration view"""
if request.user.is_authenticated:
return redirect('dashboard')
if request.method == 'POST':
form = CustomUserCreationForm(request.POST)
if form.is_valid():
user = form.save()
# Log registration
import logging
logger = logging.getLogger('auth')
logger.info(f"New user registered: {user.username}")
# Auto-login
login(request, user)
messages.success(
request,
f'Welcome, {user.get_full_name() or user.username}! '
'Your account has been created.'
)
return redirect('dashboard')
else:
form = CustomUserCreationForm()
return render(request, 'auth/register.html', {'form': form})
# User profile management views
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
class UserProfileForm(forms.ModelForm):
"""Form for editing user profile"""
class Meta:
model = User
fields = ['first_name', 'last_name', 'email']
def clean_email(self):
"""Validate email uniqueness"""
email = self.cleaned_data['email']
# Exclude current user from uniqueness check
if User.objects.filter(email=email).exclude(pk=self.instance.pk).exists():
raise forms.ValidationError("A user with this email already exists.")
return email
class UserProfileView(LoginRequiredMixin, UpdateView):
"""User profile editing view"""
model = User
form_class = UserProfileForm
template_name = 'auth/profile.html'
success_url = reverse_lazy('profile')
def get_object(self):
"""Return current user as object"""
return self.request.user
def form_valid(self, form):
"""Handle successful profile update"""
response = super().form_valid(form)
messages.success(
self.request,
'Your profile has been updated successfully.'
)
return response
class UserProfileDetailView(LoginRequiredMixin, DetailView):
"""View user profile details"""
model = User
template_name = 'auth/profile_detail.html'
context_object_name = 'profile_user'
def get_object(self):
"""Return current user as object"""
return self.request.user
def get_context_data(self, **kwargs):
"""Add additional context"""
context = super().get_context_data(**kwargs)
user = self.object
context.update({
'user_stats': {
'date_joined': user.date_joined,
'last_login': user.last_login,
'is_staff': user.is_staff,
'groups': user.groups.all(),
},
'recent_activity': self.get_recent_activity(user),
})
return context
def get_recent_activity(self, user):
"""Get user's recent activity"""
# This would depend on your activity tracking system
# Example implementation:
activity = []
# Recent posts
if hasattr(user, 'posts'):
recent_posts = user.posts.order_by('-created_at')[:5]
for post in recent_posts:
activity.append({
'type': 'post_created',
'description': f'Created post: {post.title}',
'timestamp': post.created_at,
})
# Recent comments
if hasattr(user, 'comments'):
recent_comments = user.comments.order_by('-created_at')[:5]
for comment in recent_comments:
activity.append({
'type': 'comment_created',
'description': f'Commented on: {comment.post.title}',
'timestamp': comment.created_at,
})
# Sort by timestamp
activity.sort(key=lambda x: x['timestamp'], reverse=True)
return activity[:10] # Return latest 10 activities
@login_required
def profile_view(request):
"""Function-based profile view"""
if request.method == 'POST':
form = UserProfileForm(request.POST, instance=request.user)
if form.is_valid():
form.save()
messages.success(request, 'Profile updated successfully.')
return redirect('profile')
else:
form = UserProfileForm(instance=request.user)
context = {
'form': form,
'user': request.user,
}
return render(request, 'auth/profile.html', context)
# Account management and security views
class AccountSettingsView(LoginRequiredMixin, TemplateView):
"""Account settings dashboard"""
template_name = 'auth/account_settings.html'
def get_context_data(self, **kwargs):
"""Add account information to context"""
context = super().get_context_data(**kwargs)
user = self.request.user
context.update({
'user': user,
'security_info': {
'password_last_changed': getattr(user, 'password_changed_at', None),
'two_factor_enabled': getattr(user.profile, 'two_factor_enabled', False) if hasattr(user, 'profile') else False,
'active_sessions': self.get_active_sessions(user),
},
'privacy_settings': self.get_privacy_settings(user),
'notification_settings': self.get_notification_settings(user),
})
return context
def get_active_sessions(self, user):
"""Get user's active sessions"""
from django.contrib.sessions.models import Session
active_sessions = []
current_session_key = self.request.session.session_key
for session in Session.objects.filter(expire_date__gt=timezone.now()):
try:
session_data = session.get_decoded()
session_user_id = session_data.get('_auth_user_id')
if session_user_id and int(session_user_id) == user.id:
active_sessions.append({
'session_key': session.session_key,
'is_current': session.session_key == current_session_key,
'expire_date': session.expire_date,
'ip_address': session_data.get('ip_address', 'Unknown'),
'user_agent': session_data.get('user_agent', 'Unknown')[:50],
})
except:
continue
return active_sessions
def get_privacy_settings(self, user):
"""Get user's privacy settings"""
# This would depend on your privacy settings implementation
if hasattr(user, 'profile'):
return {
'profile_public': getattr(user.profile, 'profile_public', True),
'email_public': getattr(user.profile, 'email_public', False),
'activity_public': getattr(user.profile, 'activity_public', True),
}
return {}
def get_notification_settings(self, user):
"""Get user's notification settings"""
# This would depend on your notification system
if hasattr(user, 'profile'):
return {
'email_notifications': getattr(user.profile, 'email_notifications', True),
'push_notifications': getattr(user.profile, 'push_notifications', False),
'marketing_emails': getattr(user.profile, 'marketing_emails', False),
}
return {}
class TerminateSessionView(LoginRequiredMixin, View):
"""Terminate specific user session"""
def post(self, request, *args, **kwargs):
"""Terminate session"""
session_key = request.POST.get('session_key')
if session_key and session_key != request.session.session_key:
try:
from django.contrib.sessions.models import Session
session = Session.objects.get(session_key=session_key)
# Verify session belongs to current user
session_data = session.get_decoded()
session_user_id = session_data.get('_auth_user_id')
if session_user_id and int(session_user_id) == request.user.id:
session.delete()
messages.success(request, 'Session terminated successfully.')
else:
messages.error(request, 'Invalid session.')
except Session.DoesNotExist:
messages.error(request, 'Session not found.')
else:
messages.error(request, 'Cannot terminate current session.')
return redirect('account_settings')
class DeleteAccountView(LoginRequiredMixin, TemplateView):
"""Account deletion view"""
template_name = 'auth/delete_account.html'
def post(self, request, *args, **kwargs):
"""Handle account deletion"""
password = request.POST.get('password')
confirmation = request.POST.get('confirmation')
# Verify password
if not request.user.check_password(password):
messages.error(request, 'Incorrect password.')
return self.get(request, *args, **kwargs)
# Verify confirmation
if confirmation != 'DELETE':
messages.error(request, 'Please type DELETE to confirm.')
return self.get(request, *args, **kwargs)
# Log account deletion
import logging
logger = logging.getLogger('auth')
logger.info(f"Account deleted: {request.user.username}")
# Store user info before deletion
username = request.user.username
# Delete user account
request.user.delete()
# Log out and redirect
logout(request)
messages.success(
request,
f'Account {username} has been deleted successfully.'
)
return redirect('home')
# Two-factor authentication setup
class TwoFactorSetupView(LoginRequiredMixin, TemplateView):
"""Two-factor authentication setup"""
template_name = 'auth/2fa_setup.html'
def get_context_data(self, **kwargs):
"""Add 2FA setup context"""
context = super().get_context_data(**kwargs)
user = self.request.user
# Generate TOTP secret if not exists
if not hasattr(user, 'profile') or not user.profile.totp_secret:
import pyotp
secret = pyotp.random_base32()
if hasattr(user, 'profile'):
user.profile.totp_secret = secret
user.profile.save()
else:
secret = user.profile.totp_secret
# Generate QR code
import pyotp
import qrcode
from io import BytesIO
import base64
totp_uri = pyotp.totp.TOTP(secret).provisioning_uri(
name=user.email,
issuer_name="Your Site Name"
)
qr = qrcode.QRCode(version=1, box_size=10, border=5)
qr.add_data(totp_uri)
qr.make(fit=True)
img = qr.make_image(fill_color="black", back_color="white")
buffer = BytesIO()
img.save(buffer, format='PNG')
buffer.seek(0)
qr_code_data = base64.b64encode(buffer.getvalue()).decode()
context.update({
'secret': secret,
'qr_code': qr_code_data,
'totp_uri': totp_uri,
})
return context
def post(self, request, *args, **kwargs):
"""Verify and enable 2FA"""
token = request.POST.get('token')
if hasattr(request.user, 'profile') and request.user.profile.totp_secret:
import pyotp
totp = pyotp.TOTP(request.user.profile.totp_secret)
if totp.verify(token, valid_window=1):
# Enable 2FA
request.user.profile.two_factor_enabled = True
request.user.profile.save()
messages.success(
request,
'Two-factor authentication has been enabled successfully.'
)
return redirect('account_settings')
else:
messages.error(request, 'Invalid verification code.')
else:
messages.error(request, 'Two-factor authentication not properly configured.')
return self.get(request, *args, **kwargs)
Authentication views form the user-facing interface of your authentication system. By implementing comprehensive, secure, and user-friendly authentication views, you provide users with a smooth experience while maintaining strong security standards throughout the authentication process.
Password Management
Secure password management is critical for protecting user accounts and maintaining application security. Django provides robust password handling capabilities including hashing, validation, and secure storage. Understanding these features enables you to implement strong password policies and protect user credentials.
Login and Logout
Implementing secure and user-friendly login and logout functionality is fundamental to any Django application. Understanding how to handle authentication flows, manage sessions, and provide a smooth user experience while maintaining security is essential for building robust authentication systems.