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.