django-auth

SKILL.md

Django Authentication & Permissions

You are a Django authentication expert. Your goal is to help implement secure, maintainable auth systems.

Initial Assessment

Check for project context first: If .agents/django-project-context.md exists, read it for the existing User model location, auth backend, and permissions approach.

Critical first question: Does this project already have a custom User model?

  • If yes: Read it before suggesting changes.
  • If no and migrations exist: Explain that adding one now requires careful steps.
  • If new project: Always set up a custom User model before first migration.

Custom User Model (Always Do This First)

Do this before any migrate command in a new project. It's very hard to add later.

Option A: AbstractUser (Recommended — extends default User)

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

class User(AbstractUser):
    """Custom user model — add fields here as needed."""
    email = models.EmailField(unique=True)  # Make email unique if needed
    bio = models.TextField(blank=True)
    avatar = models.ImageField(upload_to='avatars/', blank=True)

    # Optional: use email as the login field
    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['username']  # Required for createsuperuser

    def __str__(self):
        return self.email

Option B: AbstractBaseUser (Full control — more work)

Use when you need completely custom fields and don't want Django's built-in username/first_name/last_name.

from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin

class UserManager(BaseUserManager):
    def create_user(self, email, password=None, **extra_fields):
        if not email:
            raise ValueError('Email is required')
        email = self.normalize_email(email)
        user = self.model(email=email, **extra_fields)
        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_superuser(self, email, password, **extra_fields):
        extra_fields.setdefault('is_staff', True)
        extra_fields.setdefault('is_superuser', True)
        return self.create_user(email, password, **extra_fields)

class User(AbstractBaseUser, PermissionsMixin):
    email = models.EmailField(unique=True)
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    is_active = models.BooleanField(default=True)
    is_staff = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []

    objects = UserManager()

Register in Settings

# settings.py
AUTH_USER_MODEL = 'users.User'  # Always use this — never import User directly

Always Reference AUTH_USER_MODEL

# In models:
from django.conf import settings
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)

# In code (when you need the actual class):
from django.contrib.auth import get_user_model
User = get_user_model()

Django's Permission System

Model-Level Permissions (Built-in)

Django auto-creates these for every model:

  • app.add_modelname
  • app.change_modelname
  • app.delete_modelname
  • app.view_modelname

Custom Permissions

class Article(models.Model):
    class Meta:
        permissions = [
            ('publish_article', 'Can publish articles'),
            ('feature_article', 'Can feature articles on homepage'),
        ]

Checking Permissions

# In views (FBV):
from django.contrib.auth.decorators import permission_required

@permission_required('articles.publish_article', raise_exception=True)
def publish_article(request, pk):
    ...

# In CBV:
from django.contrib.auth.mixins import PermissionRequiredMixin

class PublishArticleView(PermissionRequiredMixin, View):
    permission_required = 'articles.publish_article'

# In code:
if request.user.has_perm('articles.publish_article'):
    ...

# In templates:
{% if perms.articles.publish_article %}
    <button>Publish</button>
{% endif %}

Groups

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

# Create group with permissions
editors = Group.objects.create(name='Editors')
perm = Permission.objects.get(codename='publish_article')
editors.permissions.add(perm)

# Assign user to group
user.groups.add(editors)

Authentication Backends

Default Session-Based Auth

Works out of the box. No configuration needed.

# views.py
from django.contrib.auth import authenticate, login, logout

def login_view(request):
    if request.method == 'POST':
        email = request.POST['email']
        password = request.POST['password']
        user = authenticate(request, username=email, password=password)
        if user:
            login(request, user)
            return redirect('dashboard')
        # Handle invalid credentials

Custom Backend (e.g., email login)

# users/backends.py
from django.contrib.auth import get_user_model

class EmailBackend:
    def authenticate(self, request, username=None, password=None):
        User = get_user_model()
        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):
        User = get_user_model()
        try:
            return User.objects.get(pk=user_id)
        except User.DoesNotExist:
            return None

# settings.py
AUTHENTICATION_BACKENDS = ['users.backends.EmailBackend']

django-allauth (Social + Email Auth)

Setup

pip install django-allauth
# settings.py
INSTALLED_APPS += [
    'django.contrib.sites',
    'allauth',
    'allauth.account',
    'allauth.socialaccount',
    'allauth.socialaccount.providers.google',  # Add providers
]

SITE_ID = 1
AUTHENTICATION_BACKENDS = ['allauth.account.auth_backends.AuthenticationBackend']

# allauth settings
ACCOUNT_LOGIN_METHODS = {'email'}          # Use email for login
ACCOUNT_SIGNUP_FIELDS = ['email*', 'password1*', 'password2*']
ACCOUNT_EMAIL_VERIFICATION = 'mandatory'   # 'optional' or 'none'
LOGIN_REDIRECT_URL = '/dashboard/'
# urls.py
urlpatterns += [path('accounts/', include('allauth.urls'))]

Password Validation

# settings.py
AUTH_PASSWORD_VALIDATORS = [
    {'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator'},
    {'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 'OPTIONS': {'min_length': 10}},
    {'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator'},
    {'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator'},
]

Related Skills

  • django-drf: JWT and Token authentication for REST APIs
  • django-models: Custom User model field design
  • django-admin: Register custom User in admin
  • django-views: LoginRequiredMixin and PermissionRequiredMixin
Weekly Installs
3
First Seen
5 days ago
Installed on
cline3
gemini-cli3
github-copilot3
codex3
kimi-cli3
cursor3