Django

Django Authentication & Authorization👨‍💻

Django ships with a complete authentication and authorization framework that handles user accounts, groups, permissions, and cookie-based sessions out of the box. Instead of building auth from scratch, you get a battle-tested system used by sites like Instagram, Mozilla, and Disqus.

The framework has two distinct responsibilities: authentication (verifying who a user is) and authorization (determining what they can do). Authentication flows through a middleware pipeline -- SessionMiddleware manages sessions, AuthenticationMiddleware attaches a user attribute to every request. Authorization is handled by a permission system that supports model-level permissions, group-based roles, and custom permissions. In Django 6, password hashing defaults to PBKDF2 with 1,200,000 iterations, and Python 3.12+ is required.

Key Takeaways

  • 1Always create a custom user model before running your first migration -- even `class User(AbstractUser): pass` saves you from painful migrations later
  • 2Django's authentication pipeline flows through middleware: SessionMiddleware -> AuthenticationMiddleware -> View, attaching `request.user` on every request
  • 3Authentication backends are pluggable -- write a custom backend to enable email-based login, LDAP, or token authentication by implementing `authenticate()` and `get_user()`
  • 4Django auto-creates four permissions per model (add, change, delete, view) and supports custom permissions via `Meta.permissions`
  • 5Groups implement role-based access control (RBAC) -- assign permissions to groups, then add users to groups for scalable management
  • 6Django 6 uses PBKDF2 with 1,200,000 iterations by default; Argon2 is recommended for new projects via `pip install argon2-cffi`

Master django authentication & authorization

Take the Django Authentication & Authorization course with hands-on lessons and challenges.

Examples

Custom user model with AbstractBaseUser and email login

python

AbstractBaseUser gives you full control over the user model. USERNAME_FIELD sets which field is used for login. The custom manager handles password hashing via set_password(). Always set AUTH_USER_MODEL before running your first migration -- changing it later requires resetting every migration that references User.

Protecting views with login_required and permission_required

python

login_required redirects unauthenticated users to LOGIN_URL. permission_required checks model-level permissions -- always use raise_exception=True so authenticated users without the permission get a 403 Forbidden instead of a confusing redirect to the login page.

Custom authentication backend for email-based login

python

Custom backends extend ModelBackend and override authenticate(). The timing attack protection (calling set_password even when the user is not found) ensures attackers cannot determine whether an email exists based on response time. Backends are tried in order -- keep ModelBackend as a fallback.

Group-based permissions and role management

python

Groups implement RBAC. Assign permissions to groups rather than individual users so you can update access for all members at once. Use get_or_create() for idempotent setup. The custom group_required decorator provides a clean way to restrict views by role.

Custom model permissions and template checks

python

Custom permissions are defined in Meta.permissions and created by migrations. Use the app_label.codename format (e.g., blog.publish_article) in has_perm() and permission_required. In templates, the perms variable provides dot-notation access without function calls. Always reference the user model with settings.AUTH_USER_MODEL in ForeignKey fields.

Common Mistakes

Mistake:

Not creating a custom user model before the first migration -- then needing to add fields later requires resetting all migrations

Fix:

Always define a custom user model at the start of every Django project, even if it is just `class User(AbstractUser): pass`. Set AUTH_USER_MODEL in settings before running `migrate` for the first time. This makes future changes trivial.

Mistake:

Using `is_authenticated` as a method call -- `if request.user.is_authenticated()` -- which raises a TypeError in Django 6

Fix:

Use `is_authenticated` as a property: `if request.user.is_authenticated:`. It has been a property (not a method) since Django 1.10, and calling it as a method raises an error.

Mistake:

Importing `from django.contrib.auth.models import User` directly instead of using `get_user_model()`

Fix:

Use `get_user_model()` in views, forms, and services. In model ForeignKeys, use `settings.AUTH_USER_MODEL` (a string). This ensures your code works with any custom user model.

Mistake:

Using `permission_required` without `raise_exception=True` -- authenticated users who lack the permission get silently redirected to the login page instead of seeing a 403

Fix:

Always pass `raise_exception=True` to `permission_required` so authorized-but-unpermissioned users get a 403 Forbidden response: `@permission_required('app.perm', raise_exception=True)`.

Best Practices

  • Create a custom user model before your first migration -- even `class User(AbstractUser): pass` -- to avoid painful mid-project changes
  • Use Argon2 as your primary password hasher (`pip install argon2-cffi`) and keep PBKDF2 in PASSWORD_HASHERS as a fallback for existing passwords
  • Assign permissions to Groups, not individual users -- this makes role management scalable and auditable
  • Always use `get_user_model()` in views and `settings.AUTH_USER_MODEL` in model ForeignKeys to support custom user models
  • Stack `@login_required` with `@permission_required(raise_exception=True)` on protected views for clear, declarative access control
  • Automate group and permission setup in a management command so it is version-controlled and consistent across environments

Summary

Django's authentication framework handles the full lifecycle: middleware-driven session management, pluggable authentication backends, and a comprehensive permission system with groups. Always start a project with a custom user model (AbstractUser or AbstractBaseUser) and set AUTH_USER_MODEL before migrating. Use login_required and permission_required decorators for declarative access control. Assign permissions to Groups for scalable RBAC. Django 6 defaults to PBKDF2 with 1,200,000 iterations, but Argon2 is recommended for new projects.

Practice Django with hands-on challenges

Learn django authentication & authorization hands-on in your IDE

Interactive lessons and challenges on Stanza, practice in VS Code, Cursor, or the web.

Related Concepts

Related Cheatsheets

Master Django with Stanza

Interactive lessons and challenges, right in your code editor.

Check the free courses. No credit card.