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.
Master django authentication & authorization
Take the Django Authentication & Authorization course with hands-on lessons and challenges.
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.
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 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.
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 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.
Not creating a custom user model before the first migration -- then needing to add fields later requires resetting all migrations
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.
Using `is_authenticated` as a method call -- `if request.user.is_authenticated()` -- which raises a TypeError in Django 6
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.
Importing `from django.contrib.auth.models import User` directly instead of using `get_user_model()`
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.
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
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)`.
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.
Interactive lessons and challenges on Stanza, practice in VS Code, Cursor, or the web.
Interactive lessons and challenges, right in your code editor.
Check the free courses. No credit card.