User Authentication & Permissions in Django
Django ships with a powerful authentication and authorization system. This guide covers the key parts you need to know and the practices that keep real projects secure and maintainable.
1. Use Django's Built-In Auth System
Django provides django.contrib.auth out of the box. It handles:
- User model (username, password, email, etc.)
- Hashed password storage
- Login, logout, password reset views
- Permissions and groups
Do not reinvent this. Use it.
# settings.py
INSTALLED_APPS = [
'django.contrib.auth',
'django.contrib.contenttypes',
# ...
]
Reference: Django Authentication Documentation
2. Create a Custom User Model Early
Even if you think the default User model is fine now, create a custom one from day one. Changing user models later is painful.
# accounts/models.py
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
# add custom fields later if needed
pass
# settings.py
AUTH_USER_MODEL = 'accounts.User'
Run migrations before creating any other models that reference the user.
Reference: Customizing Authentication in Django
3. Reference Users Correctly
Never hardcode User imports. Use Django's helper:
from django.contrib.auth import get_user_model
User = get_user_model()
For foreign keys:
from django.conf import settings
class Article(models.Model):
author = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE
)
4. Protect Views With Decorators
Use @login_required for views that need authentication:
from django.contrib.auth.decorators import login_required
@login_required
def dashboard(request):
return render(request, 'dashboard.html')
For permission checks, use @permission_required:
from django.contrib.auth.decorators import permission_required
@login_required
@permission_required('blog.add_post', raise_exception=True)
def create_post(request):
# only users with 'add_post' permission can access
pass
Reference: Using the Django Authentication System
5. Use Class-Based View Mixins
For class-based views, use mixins instead of decorators:
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
from django.views.generic import CreateView
class PostCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
permission_required = 'blog.add_post'
model = Post
fields = ['title', 'content']
6. Understand Django's Permission System
Django auto-creates four permissions per model:
add_modelnamechange_modelnamedelete_modelnameview_modelname
Check permissions in code:
if request.user.has_perm('blog.change_post'):
# allow editing
Reference: Permissions in Django (TestDriven.io)
7. Add Custom Permissions When Needed
Define extra permissions in your model's Meta class:
class Post(models.Model):
title = models.CharField(max_length=200)
published = models.BooleanField(default=False)
class Meta:
permissions = [
('publish_post', 'Can publish posts'),
('feature_post', 'Can feature posts on homepage'),
]
Then check with user.has_perm('blog.publish_post').
8. Use Groups For Role-Based Access
Instead of assigning permissions to individual users, create groups:
from django.contrib.auth.models import Group, Permission
# Create group
editors = Group.objects.create(name='Editors')
# Add permission to group
perm = Permission.objects.get(codename='change_post')
editors.permissions.add(perm)
# Add user to group
user.groups.add(editors)
Users inherit all permissions from their groups.
9. Object-Level Permissions
Django's default permissions are model-level. For object-level (e.g., "user can only edit their own posts"), check manually or use a library like django-guardian.
Simple manual check:
def edit_post(request, post_id):
post = get_object_or_404(Post, id=post_id)
if post.author != request.user:
return HttpResponseForbidden("Not your post")
# proceed with edit
Reference: django-guardian Documentation
10. Enforce Strong Passwords
Django has built-in password validators. Keep them enabled:
# settings.py
AUTH_PASSWORD_VALIDATORS = [
{'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator'},
{'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator'},
{'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator'},
{'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator'},
]
Reference: Security in Django
11. Add Two-Factor Authentication (2FA)
For sensitive apps, add 2FA using django-otp or django-two-factor-auth:
pip install django-two-factor-auth
This adds TOTP (app-based codes like Google Authenticator) and backup tokens.
Reference: django-two-factor-auth Documentation
12. JWT For APIs (Django REST Framework)
For token-based API auth, use djangorestframework-simplejwt:
pip install djangorestframework-simplejwt
# settings.py
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework_simplejwt.authentication.JWTAuthentication',
],
}
from datetime import timedelta
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=15),
'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
}
Keep access tokens short-lived. Use refresh tokens to get new access tokens.
Reference: Simple JWT Documentation
13. DRF Permissions For APIs
Django REST Framework has its own permission classes:
from rest_framework.permissions import IsAuthenticated, IsAdminUser
class PostViewSet(viewsets.ModelViewSet):
permission_classes = [IsAuthenticated]
For custom logic (e.g., owner-only edit):
from rest_framework import permissions
class IsOwnerOrReadOnly(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
return True
return obj.author == request.user
Reference: DRF Permissions Guide
14. Quick Security Checklist
- Use HTTPS in production
- Set
SESSION_COOKIE_SECURE = TrueandCSRF_COOKIE_SECURE = True - Keep
DEBUG = Falsein production - Use environment variables for
SECRET_KEY - Enable password validators
- Log failed login attempts
- Consider rate limiting on login endpoints
Reference: Django Security Best Practices (Escape.tech)
15. Summary: What To Do
- Start with a custom user model
- Use
@login_requiredand@permission_requireddecorators - Create groups for roles, assign permissions to groups
- For object-level access, check ownership manually or use django-guardian
- For APIs, use DRF permissions and JWT with short token lifetimes
- Enable 2FA for sensitive applications
Django's auth system is battle-tested. Use it properly, extend it when needed, and avoid building your own unless you have a very specific reason.