Back to Blog

User Authentication & Permissions in Django

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

Login Security

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

User Permissions

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_modelname
  • change_modelname
  • delete_modelname
  • view_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 = True and CSRF_COOKIE_SECURE = True
  • Keep DEBUG = False in 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_required and @permission_required decorators
  • 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.

Further Reading