Authentication & permissions

Django ships a complete authentication system: a User model, password hashing, sessions, login views and a permission framework. Learning to use it well saves you from writing (and securing) your own.

Login and logout with built-in views

# config/urls.py
from django.urls import include, path

urlpatterns = [
    path("accounts/", include("django.contrib.auth.urls")),
    # provides login/, logout/, password_change/, password_reset/ ...
]
# config/settings.py
LOGIN_URL = "login"
LOGIN_REDIRECT_URL = "dashboard"
LOGOUT_REDIRECT_URL = "home"

Create templates/registration/login.html with a form that posts to {% url 'login' %}. Since Django 5.0 the logout view only accepts POST, so log out with a small form containing {% csrf_token %}, not a plain link.

A custom user model from day one

Start every new project with a custom user model, even if it is empty. Switching later is painful.

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

class User(AbstractUser):
    phone = models.CharField(max_length=15, blank=True)

# settings.py
AUTH_USER_MODEL = "accounts.User"

Refer to it in code with get_user_model() or, in model fields, settings.AUTH_USER_MODEL.

Protecting views

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

@login_required
def dashboard(request):
    return render(request, "dashboard.html", {"orders": request.user.orders.all()})

@permission_required("shop.change_product", raise_exception=True)
def edit_price(request, pk):
    ...

For class-based views use LoginRequiredMixin and PermissionRequiredMixin, listed before the view class.

Permissions and groups

Every model automatically gets four permissions: add_, change_, delete_ and view_, named like shop.change_product. Assign them to groups (roles) and put users in groups.

Group Permissions
Cashier view_product, add_order
Store manager all of the above + change_product, change_order
Accountant view_order, custom export_gst_report
class Order(models.Model):
    ...
    class Meta:
        permissions = [("export_gst_report", "Can export GST report")]

Check permissions anywhere with request.user.has_perm("shop.export_gst_report"), and in templates with {% if perms.shop.change_product %}.

Object-level rules

Built-in permissions are per model, not per row. For "users may only edit their own orders", filter the queryset:

from django.shortcuts import get_object_or_404

@login_required
def order_detail(request, pk):
    order = get_object_or_404(Order, pk=pk, customer=request.user)   # 404 for other people's orders
    return render(request, "shop/order_detail.html", {"order": order})

Key takeaways

  • Use the built-in auth views and password hashers; never store or compare raw passwords.
  • Create a custom user model before your first migration.
  • Group permissions into roles instead of assigning them user by user.
  • Enforce ownership by filtering querysets, and remember hiding a button in a template is not security.

Practice

Add a Cashier and a Manager group to your shop project. Protect a price-edit view so only managers can use it, show an "Edit" button only when perms.shop.change_product is true, and make order pages visible only to their owner.

B

Written by Bablu Kumar Jha

Founder, BeeProton AI · Mumbai, Maharashtra, India