Skip to main content

User Model and Authentication Basics

The authentication system in this codebase is built around a flexible hierarchy of user models. This structure allows for everything from a standard username-based login to highly customized user entities with unique identifiers or specialized authentication logic.

User Model Hierarchy​

The system provides three levels of abstraction for defining users, located in django/contrib/auth/base_user.py and django/contrib/auth/models.py.

AbstractBaseUser​

The AbstractBaseUser class in django/contrib/auth/base_user.py is the foundation of the authentication system. it provides the essential machinery for handling passwords and session security without enforcing a specific schema for identifying users (like a username or email).

Key features include:

  • Password Management: Implements set_password() and check_password() (and their async counterparts acheck_password()), which handle hashing and verification.
  • Session Security: The get_session_auth_hash() method returns an HMAC of the password field. This is used by the session backend to invalidate sessions across all devices when a user changes their password.
  • Authentication State: Hardcodes is_authenticated to True and is_anonymous to False.

AbstractUser​

Located in django/contrib/auth/models.py, AbstractUser inherits from AbstractBaseUser and PermissionsMixin. It provides a "batteries-included" user model that includes common fields:

  • username (with UnicodeUsernameValidator)
  • first_name and last_name
  • email
  • is_staff and is_active
  • date_joined

It also defines USERNAME_FIELD = "username", which tells the authentication backends which field to use for identification.

User​

The User class is the concrete implementation of AbstractUser. In most projects, this is the model used by default. It is marked as swappable, meaning it can be replaced by a custom model via the AUTH_USER_MODEL setting.

Representing Anonymous Visitors​

The AnonymousUser class in django/contrib/auth/models.py provides a way to handle unauthenticated visitors using the same API as a real User object. This consistency allows developers to write code like request.user.is_authenticated without checking if request.user is None.

Unlike the standard user models, AnonymousUser:

  • Is not a database model (calling .save() or .delete() raises NotImplementedError).
  • Has id and pk set to None.
  • Returns True for is_anonymous and False for is_authenticated.
  • Provides empty implementations for permission checks (e.g., has_perm() always returns False).

Customizing the User Model​

Depending on the requirements, you can customize the user model by inheriting from either AbstractUser or AbstractBaseUser.

Extending AbstractUser​

If you want to keep the default fields but add extra data or change the primary key, inherit from AbstractUser. For example, tests/auth_tests/models/uuid_pk.py demonstrates using a UUID as a primary key:

from django.contrib.auth.models import AbstractUser
from django.db import models
import uuid

class User(AbstractUser):
uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)

Building from AbstractBaseUser​

If you want to completely redefine the user identification logic (e.g., using only an email or a custom token), inherit from AbstractBaseUser. You must define a USERNAME_FIELD and a REQUIRED_FIELDS list.

A minimal example from tests/auth_tests/models/no_password.py shows a user model that only requires a username:

from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager
from django.db import models

class NoPasswordUser(AbstractBaseUser):
username = models.CharField(max_length=100, unique=True)
USERNAME_FIELD = 'username'
objects = BaseUserManager()

Best Practices for Referencing Users​

Because the user model can be swapped, you should never import the User model directly in your application code. Instead, use the following patterns:

  1. In Code: Use django.contrib.auth.get_user_model() to retrieve the active user model class.
  2. In Models: When defining a ForeignKey to the user model, use settings.AUTH_USER_MODEL.

The get_user_model() function in django/contrib/auth/__init__.py ensures you are always interacting with the model defined in the project's configuration:

def get_user_model():
"""
Return the User model that is active in this project.
"""
try:
return django_apps.get_model(settings.AUTH_USER_MODEL, require_ready=False)
except ValueError:
# ... error handling ...

This approach ensures that your application remains compatible even if the project switches to a custom user model later.