Introduction to Django’s Authentication System
This article provides a beginner-friendly introduction to Django’s authentication system. It explains users, passwords, sessions, login and logout, permissions, groups, built-in authentication views, access control for function-based and class-based views, registration, password management, and the role of custom user models.
Introduction to Django’s Authentication System
Most web applications need to know who is using them.
A website may allow visitors to read public pages while requiring an account to create posts, update profiles, place orders, or access administrative tools. Some authenticated users may have additional permissions that allow them to perform actions unavailable to everyone else.
Django includes an authentication system that provides the basic components needed to manage this behavior.
It supports:
- user accounts
- passwords
- login and logout
- cookie-based sessions
- permissions
- groups
- authentication forms and views
- access restrictions
- customizable authentication backends
Django’s authentication framework is provided primarily by the django.contrib.auth application. It handles both authentication, which determines who a user is, and authorization, which determines what that user is allowed to do.
Authentication and Authorization
Authentication and authorization are related, but they answer different questions.
Authentication asks:
Who is this user?
Authorization asks:
What is this user allowed to do?
For example, a user may successfully log in with a username and password. That means the user has been authenticated.
The application may then check whether that user has permission to delete an article. That is authorization.
A user can therefore be:
- unauthenticated
- authenticated without a particular permission
- authenticated with a particular permission
- a staff user
- a superuser
Django’s auth system provides tools for handling each of these cases.
The Main Parts of Django Authentication
The default authentication system is built around several related components.
| Component | Purpose |
|---|---|
| Users | Represent people or accounts |
| Passwords | Verify user credentials securely |
| Sessions | Keep users logged in across requests |
| Permissions | Allow or deny specific actions |
| Groups | Apply permissions to multiple users |
| Authentication backends | Decide how credentials are verified |
| Forms and views | Support login, logout, and password management |
| Decorators and mixins | Restrict access to views |
These components work together, but they can also be customized separately.
Default Authentication Configuration
A project created with django-admin startproject normally includes the main authentication configuration.
The relevant installed applications include:
⧉
1 2 3 4 5 6 7 | |
The relevant middleware includes:
⧉
1 2 3 4 5 6 | |
SessionMiddleware manages session data across requests.
AuthenticationMiddleware uses the session to associate a user with each incoming request. The standard Django project configuration includes these components by default.
Run migrations to create the required database tables:
⧉
1 | |
This creates tables used for:
- users
- groups
- permissions
- sessions
- content types
- migration records
The Default User Model
Django provides a default user model named User.
It can be imported directly:
⧉
1 | |
A user can contain information such as:
- username
- password
- first name
- last name
- email address
- staff status
- active status
- superuser status
- groups
- individual permissions
- account creation date
- last login date
A user might be created in the Django shell:
⧉
1 2 3 4 5 6 7 8 | |
The create_user() method is important because it processes the password correctly before saving it.
Do not create a password by assigning plain text directly:
⧉
1 2 3 4 5 6 | |
This stores the value incorrectly and does not produce a usable Django password.
Use create_user():
⧉
1 2 3 4 | |
Or use set_password():
⧉
1 2 3 | |
Password Hashing
Django does not normally store users’ original passwords.
Instead, it stores a derived password hash containing the information needed to verify a password later.
When a user attempts to log in, Django processes the submitted password and compares the result with the stored password data.
This means passwords should be handled through Django’s user methods:
⧉
1 2 | |
Check a password with:
⧉
1 | |
Avoid reading or comparing the password field directly:
⧉
1 2 | |
That comparison will not work correctly because user.password contains encoded password information rather than the original password.
Creating a Superuser
A superuser has all permissions and can access the Django administration site when the admin application is configured.
Create one with:
⧉
1 | |
Django prompts for account information such as:
⧉
1 2 3 4 | |
The resulting account normally has:
⧉
1 2 | |
A superuser can usually manage users, groups, permissions, and registered models through the Django admin.
Accessing the Current User
Django adds the current user to the request object:
⧉
1 | |
For a logged-in visitor, request.user is a user-model instance.
For a visitor who is not logged in, it is an AnonymousUser instance.
Check the user with:
⧉
1 2 3 4 5 | |
is_authenticated is a property, not a method.
Correct:
⧉
1 | |
Incorrect:
⧉
1 | |
Django uses sessions and authentication middleware to provide request.user. An unauthenticated request receives AnonymousUser; an authenticated request receives the relevant user instance.
AnonymousUser
An unauthenticated visitor is represented by AnonymousUser.
This allows code to access request.user without first checking whether a user object exists.
For example:
⧉
1 2 3 4 5 | |
Useful differences include:
⧉
1 2 3 4 5 6 7 | |
Prefer checking:
⧉
1 | |
rather than checking the user’s exact class.
Authenticating Credentials
Django provides the authenticate() function for checking credentials.
⧉
1 2 3 4 5 6 7 8 | |
If the credentials are accepted, authenticate() returns a user object.
If authentication fails, it returns None.
⧉
1 2 3 4 5 6 7 8 9 10 11 12 | |
Authentication does not automatically log the user in.
It only verifies the credentials and returns the matching user.
Logging a User In
Use Django’s login() function to attach an authenticated user to the current session.
⧉
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
The common sequence is:
⧉
1 2 3 4 5 6 7 8 9 | |
login() records the authenticated user in Django’s session framework so the user remains associated with later requests.
A Basic Login View
A complete basic login view might look like this:
⧉
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | |
A simple template could be:
⧉
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | |
This example demonstrates the basic process, but Django also provides built-in forms and views that avoid rewriting standard authentication behavior.
Logging a User Out
Use logout() to end the authenticated session:
⧉
1 2 3 4 5 6 7 | |
After logout:
⧉
1 | |
will be False on subsequent requests.
Logout should normally be triggered by a POST request rather than a plain link using GET.
Example:
⧉
1 2 3 4 5 6 7 8 9 | |
Template:
⧉
1 2 3 4 | |
Django’s Built-In Authentication Views
Django includes class-based views for common authentication tasks.
These include views for:
- login
- logout
- changing passwords
- resetting forgotten passwords
- confirming password resets
They are available from:
⧉
1 | |
Common classes include:
⧉
1 2 3 4 5 6 7 8 | |
Using the built-in views reduces repeated authentication code and uses Django’s standard behavior.
Adding Authentication URLs
Django provides a ready-made authentication URL configuration.
Include it in the project URL configuration:
⧉
1 2 3 4 5 6 | |
This creates named routes such as:
⧉
1 2 3 4 5 6 7 8 | |
The exact routes are provided by Django’s auth URL configuration.
This approach gives the application standard authentication behavior while allowing custom templates.
Login Templates
Django’s built-in LoginView uses this template by default:
⧉
1 | |
A project structure might contain:
⧉
1 2 3 4 | |
Example template:
⧉
1 2 3 4 5 6 7 8 9 10 11 12 | |
Ensure the project template directory is configured:
⧉
1 2 3 4 5 6 7 | |
Configuring Login Redirects
After login, Django may redirect the user to the URL provided in the next parameter.
For example:
⧉
1 | |
After successful authentication, the user is redirected to:
⧉
1 | |
A default redirect can be configured in settings.py:
⧉
1 | |
The login page can also be configured:
⧉
1 | |
The logout redirect can be configured with:
⧉
1 | |
Using named URL patterns keeps the configuration independent of hard-coded paths.
Restricting Function-Based Views
Use the login_required decorator to prevent unauthenticated visitors from accessing a function-based view.
⧉
1 2 3 4 5 6 7 | |
When an unauthenticated visitor requests this page, Django redirects them to the configured login URL.
The original destination is added as a next parameter:
⧉
1 | |
After login, the user can be returned to the requested page.
A custom login URL can be supplied:
⧉
1 2 3 | |
Restricting Class-Based Views
Use LoginRequiredMixin with class-based views:
⧉
1 2 3 4 5 6 7 8 9 | |
The mixin should normally appear before the main view class:
⧉
1 2 3 4 5 | |
URL pattern:
⧉
1 2 3 4 5 6 7 8 9 10 11 12 | |
Authentication Checks in Templates
When Django’s authentication context processor is enabled, templates can access the current user through:
⧉
1 | |
Check whether the user is authenticated:
⧉
1 2 3 4 5 | |
Show login or logout controls:
⧉
1 2 3 4 5 6 7 8 | |
Authentication checks in templates control presentation.
They do not secure a view by themselves.
This is not sufficient:
⧉
1 2 3 | |
The dashboard view must also enforce authentication with login_required or LoginRequiredMixin.
Hiding a link is not the same as protecting the destination.
Registering Users
Django includes authentication views, but it does not automatically add a complete public registration workflow to every project.
A basic registration form can use UserCreationForm:
⧉
1 | |
Example view:
⧉
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | |
Template:
⧉
1 2 3 4 5 6 7 8 | |
URL pattern:
⧉
1 | |
UserCreationForm handles:
- username validation
- password confirmation
- password validation
- secure password processing
- user creation
User Status Fields
The default user model includes several status fields.
is_active
Indicates whether the account should be treated as active:
⧉
1 | |
An inactive account is usually prevented from authenticating by Django’s default authentication backend.
An account can be disabled without deleting it:
⧉
1 2 | |
This can be useful when account history and related records should be preserved.
is_staff
Controls whether a user may access the Django admin:
⧉
1 | |
Staff status alone does not automatically grant every permission.
is_superuser
Indicates that the user has all permissions without requiring them to be assigned individually:
⧉
1 | |
A common misunderstanding is that every authenticated user is a staff user.
These states are separate:
⧉
1 2 3 4 5 6 7 8 | |
Permissions
Django includes a model-level permission system.
For each model, Django creates standard permissions such as:
⧉
1 2 3 4 | |
For a model named Article in an app named articles, the permissions are typically:
⧉
1 2 3 4 | |
Check a permission with:
⧉
1 | |
Example:
⧉
1 2 3 | |
A user can receive permissions:
- directly
- through one or more groups
- automatically as a superuser
Restricting Views by Permission
For function-based views, use permission_required:
⧉
1 2 3 4 5 6 7 8 9 | |
With raise_exception=True, Django raises PermissionDenied when the user lacks the permission.
Without it, the user may be redirected to the login page.
For class-based views, use PermissionRequiredMixin:
⧉
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
Multiple permissions can be required:
⧉
1 2 3 4 | |
Groups
A group is a collection of permissions that can be assigned to multiple users.
Examples of application groups might include:
⧉
1 2 3 4 | |
Instead of assigning the same permissions to every editor individually, create an Editors group and assign permissions to that group.
Users added to the group receive its permissions.
⧉
1 2 3 4 5 | |
Check group membership:
⧉
1 2 3 | |
However, permission checks are usually preferable to group-name checks when access depends on a specific capability.
Prefer:
⧉
1 | |
over:
⧉
1 | |
The permission check describes what the user may do rather than how the permission was assigned.
Adding Permissions to a User
A permission can be assigned directly:
⧉
1 2 3 4 5 6 7 8 | |
Remove it with:
⧉
1 | |
Retrieve all effective permissions:
⧉
1 | |
Check several permissions:
⧉
1 2 3 4 5 6 | |
Custom Permissions
Models can define additional permissions through Meta.
⧉
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
Create and apply a migration:
⧉
1 2 | |
The permission can then be checked with:
⧉
1 2 3 | |
Custom permissions are useful for domain-specific actions that do not match the standard add, change, delete, and view permissions.
Authentication Backends
An authentication backend determines how Django verifies credentials and checks permissions.
The default backend is commonly:
⧉
1 2 3 | |
ModelBackend authenticates against Django’s user model and supports Django’s standard permission system.
A project can define or install other backends for cases such as:
- email-based login
- company directory authentication
- external identity providers
- custom account databases
- remote-user authentication
The authenticate() function tries the configured authentication backends until one accepts the supplied credentials.
For a basic project, the default backend is usually sufficient.
Using the Configured User Model
Django allows projects to replace the default user model.
For code that refers to the user model, prefer:
⧉
1 2 3 4 | |
For model relationships, use:
⧉
1 2 3 4 5 6 7 8 9 | |
Avoid hard-coding the default user model in reusable code:
⧉
1 | |
Directly importing User may be acceptable in a small project that definitely uses Django’s default model, but get_user_model() and settings.AUTH_USER_MODEL are more flexible.
Use:
⧉
1 2 3 4 5 | |
Choosing a Custom User Model Early
Projects frequently need additional account behavior, such as:
- login by email address
- additional profile fields
- different required fields
- a different user identifier
- application-specific account methods
Django supports custom user models, but changing the user model after a project has accumulated migrations and data can be difficult.
For a new production project, it is worth deciding early whether a custom user model will be needed.
A common minimal approach extends AbstractUser:
⧉
1 2 3 4 5 | |
Then configure it before the initial migrations:
⧉
1 | |
Even an initially empty custom model provides a place for future account customization.
A full explanation of custom user models is a separate topic. Beginners should first understand Django’s default authentication workflow.
Password Change and Reset
Django includes views for changing a known password and resetting a forgotten password.
Password change is for a user who is already authenticated and knows the current password.
Password reset is for a user who cannot log in and needs a reset link sent by email.
The built-in URL configuration provides both workflows:
⧉
1 2 3 4 | |
Password reset requires email configuration because Django must send the reset link.
For local development, email can be written to the terminal:
⧉
1 2 3 | |
A reset email will then appear in the development server output instead of being sent through a real mail provider.
Sessions and Authentication
Django normally keeps users logged in with sessions.
After successful login, the session contains information Django can use to identify the authenticated user on later requests.
The browser receives a session cookie.
On later requests:
- the browser sends the session cookie
SessionMiddlewareloads the sessionAuthenticationMiddlewarefinds the associated user- Django assigns that user to
request.user
The browser’s cookie does not normally contain the user’s password.
The session connects the request to server-side authentication state.
CSRF Protection
Authentication forms that submit data should include CSRF protection.
Example:
⧉
1 2 3 4 5 6 | |
This applies to forms for:
- login
- logout
- registration
- password changes
- profile updates
- permission-protected actions
CSRF protection helps prevent another website from submitting an unwanted request using a user’s authenticated browser session.
Authentication Is Not Template Visibility
A frequent beginner mistake is securing only the user interface.
For example:
⧉
1 2 3 4 5 | |
This hides the link from anonymous visitors, but it does not prevent them from manually visiting the URL.
The view must enforce the rule:
⧉
1 2 3 | |
Or:
⧉
1 2 3 4 5 | |
The same principle applies to permissions.
A hidden button is a presentation decision.
A decorator, mixin, or explicit server-side check is an access-control decision.
Common Beginner Mistakes
Storing Plain-Text Passwords
Avoid:
⧉
1 2 | |
Use:
⧉
1 2 | |
Or:
⧉
1 2 3 4 | |
Calling is_authenticated
Incorrect:
⧉
1 | |
Correct:
⧉
1 | |
Assuming Authentication Grants Every Permission
A logged-in user does not automatically have permission to edit or delete every object.
Check the required permissions or ownership rules.
Protecting Only the Template
Hiding links does not protect URLs.
Always enforce authentication and authorization in the view.
Forgetting AuthenticationMiddleware
Without the correct middleware, request.user will not behave as expected.
Importing the Default User Everywhere
Prefer get_user_model() and settings.AUTH_USER_MODEL when the code should support a custom user model.
Changing the User Model Late
Choose a custom user model near the beginning of a new project when customization is likely.
Writing a Complete Login System From Scratch
Django already provides tested forms, views, password handling, sessions, and reset workflows.
Customize the built-in components before replacing them.
Revealing Too Much in Login Errors
A generic message is often preferable:
⧉
1 | |
Avoid confirming whether a particular username or email address exists unless the workflow requires it.
A Basic Authentication Setup
A small project can use Django’s built-in auth views with the following setup.
Project URLs:
⧉
1 2 3 4 5 6 7 8 9 10 11 12 | |
Settings:
⧉
1 2 3 | |
Login template:
⧉
1 2 3 | |
⧉
1 2 3 4 5 6 7 8 | |
Protected function-based view:
⧉
1 2 3 4 5 6 7 8 9 10 | |
Protected class-based view:
⧉
1 2 3 4 5 6 7 8 9 | |
Navigation template:
⧉
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | |
This provides a basic login, logout, and protected-page workflow.
Recommended Learning Order
A practical order for learning Django authentication is:
- Understand
request.user. - Check
is_authenticated. - Create users with
create_user(). - Use Django’s built-in login and logout views.
- Protect views with
login_required. - Protect class-based views with
LoginRequiredMixin. - Build a registration page with
UserCreationForm. - Learn model permissions.
- Learn groups.
- Add password reset.
- Learn when to use a custom user model.
- Explore custom authentication backends only when needed.
Start with Django’s default behavior before adding external authentication packages or custom account logic.
Mini Reference
⧉
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | |
Common settings:
⧉
1 2 3 | |
Common permission check:
⧉
1 2 3 | |
Common user relationship:
⧉
1 2 3 4 | |
Django’s authentication system provides the basic infrastructure needed to identify users and control access to application features.
The main concepts are:
- authentication establishes who the user is
- authorization determines what the user may do
- users are connected to requests through sessions and middleware
request.userrepresents the current userauthenticate()verifies credentialslogin()begins an authenticated sessionlogout()ends the session- decorators and mixins protect views
- permissions describe allowed actions
- groups assign permissions to multiple users
- Django provides built-in forms and views for standard account workflows
- password values should always be handled through Django’s password tools
- custom user models should be considered early in a project
For most basic applications, Django’s built-in authentication components provide a secure and practical starting point. Learn the default system first, configure the existing views and forms, and add customization only when the application has a clear requirement for it.
Join the Newsletter
Practical insights on Django, backend systems, deployment, architecture, and real-world development — delivered without noise.
Get updates when new guides, learning paths, cheat sheets, and field notes are published.
No spam. Unsubscribe anytime.
There is no third-party involved so don't worry - we won't share your details with anyone.