How To Use Django User Authentication

Sharing is caring 🙂

User authentication is the process of verifying a user’s identity when they access a system or application. It is an essential security measure that helps protect against unauthorized access and ensures that only authorized users can access sensitive information or perform certain actions. In Django, user authentication is implemented using the django.contrib.auth package, which provides a set of built-in authentication views and forms that can be easily customized to fit the needs of your application. In this article, we will explore how to use the django.contrib.auth package to add user authentication to a Django project.

Before diving into the specifics of Django’s authentication system, it is important to understand the basic concepts and components that make up user authentication in web applications. These include user accounts, passwords, and sessions.

User accounts represent individual users who have been granted access to the system or application. Each user account is typically associated with a unique username and password that are used to verify the user’s identity when they log in.

Passwords are used to protect user accounts and ensure that only authorized users can access them. Passwords should be hashed and stored in a secure way to prevent them from being compromised.

Sessions are used to maintain the state of a user’s authentication status across multiple requests. When a user logs in, a session is created to keep track of their authenticated status until they log out or the session expires.

With these concepts in mind, we can now delve into the details of how user authentication works in Django using the django.contrib.auth package.

Setting Up the Django Auth Package

To use the django.contrib.auth package in your Django project, you first need to include it in the INSTALLED_APPS list in your project’s settings.py file.

INSTALLED_APPS = [    ...    'django.contrib.auth',    ...]

Next, you will need to include the django.contrib.auth URL patterns in your project’s root urls.py file.

from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    path('admin/', admin.site.urls),
    path('accounts/', include('django.contrib.auth.urls')),
]

This will enable the built-in authentication views and forms provided by django.contrib.auth, which you can use to handle user login, logout, and registration.

It’s also a good idea to set the LOGIN_URL and LOGIN_REDIRECT_URL settings in your settings.py file. LOGIN_URL specifies the URL where users should be redirected when they need to log in, and LOGIN_REDIRECT_URL specifies the URL to redirect to after a successful login.

LOGIN_URL = '/accounts/login/'
LOGIN_REDIRECT_URL = '/'

With these steps, you should now be able to use the django.contrib.auth package to add user authentication to your Django project. In the next section, we will look at how to create a custom user model using Django’s AbstractBaseUser and BaseUserManager classes.

Creating a User Model with Django’s AbstractBaseUser and BaseUserManager

By default, Django’s django.contrib.auth package includes a User model that stores basic user information such as username, password, and email address. However, you may want to customize this model to store additional fields or implement custom behavior.

To create a custom user model, you can subclass AbstractBaseUser and BaseUserManager from the django.contrib.auth.models module. AbstractBaseUser provides the core implementation for a user model, including fields for the username, password, and email address, as well as methods for managing the user’s authentication status. BaseUserManager provides helper methods for creating and working with user objects.

Here is an example of how you might create a custom user model using AbstractBaseUser and BaseUserManager:

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

class CustomUserManager(BaseUserManager):
    def create_user(self, email, password=None, **extra_fields):
        if not email:
            raise ValueError('The Email field must be set')
        email = self.normalize_email(email)
        user = self.model(email=email, **extra_fields)
        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_superuser(self, email, password=None, **extra_fields):
        extra_fields.setdefault('is_staff', True)
        extra_fields.setdefault('is_superuser', True)
        return self.create_user(email, password, **extra_fields)

class CustomUser(AbstractBaseUser, PermissionsMixin):
    email = models.EmailField(unique=True)
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)
    is_active = models.BooleanField(default=True)
    is_staff = models.BooleanField(default=False)

    objects = CustomUserManager()

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['first_name', 'last_name']

In this example, we have defined a custom CustomUser model that includes fields for the user’s email, first name, and last name, as well as a CustomUserManager class that provides methods for creating and working with CustomUser objects.

To use this custom user model in your Django project, you will need to specify it in the AUTH_USER_MODEL setting in your settings.py file.

AUTH_USER_MODEL = 'myapp.CustomUser'

With these steps, you should now be able to use your custom user model with Django’s django.contrib.auth package to handle user authentication in your project. In the next section, we will look at how to use Django’s built-in authentication forms and views to handle user login and registration.

Using Django’s Authentication Forms and Views

Django’s django.contrib.auth package includes a set of built-in forms and views that you can use to handle user login, logout, and registration.

To handle user login, you can use the login view and AuthenticationForm form provided by django.contrib.auth. The login view displays a login form to the user and verifies their credentials when they submit the form. The AuthenticationForm form handles input validation and authentication.

Here is an example of how you might use the login view and AuthenticationForm form in a Django view function:

from django.contrib.auth import authenticate, login
from django.contrib.auth.forms import AuthenticationForm
from django.shortcuts import render, redirect

def login_view(request):
    if request.method == 'POST':
        form = AuthenticationForm(request, data=request.POST)
        if form.is_valid():
            username = form.cleaned_data.get('username')
            password = form.cleaned_data.get('password')
            user = authenticate(username=username, password=password)
            if user is not None:
                login(request, user)
                return redirect('home')
    else:
        form = AuthenticationForm()
    return render(request, 'login.html', {'form': form})

In this example, we have defined a login_view function that displays a login form to the user and authenticates their credentials when they submit the form. If the login is successful, the user is redirected to the home view.

To handle user logout, you can use the logout view provided by django.contrib.auth. The logout view destroys the user’s session and logs them out of the system.

Here is an example of how you might use the logout view in a Django view function:

from django.contrib.auth import logout
from django.shortcuts import redirect

def logout_view(request):
    logout(request)
    return redirect('login')

To handle user registration, you can create a custom form and view to handle input validation and the creation of new user accounts. Here is an example of how you might create a custom registration form and view in Django:

from django import forms
from django.contrib.auth import authenticate, login
from django.shortcuts import render, redirect


class RegistrationForm(forms.Form):
    email = forms.EmailField()
    password = forms.CharField(widget=forms.PasswordInput)
    password_confirm = forms.CharField(widget=forms.PasswordInput)

    def clean_email(self):
        email = self.cleaned_data.get('email')
        if User.objects.filter(email=email).exists():
            raise forms.ValidationError(
                'A user with that email already exists')
        return email

    def clean(self):
        cleaned_data = super().clean()
        password = cleaned_data.get('password')
        password_confirm = cleaned_data.get('password_confirm')
        if password != password_confirm:
            raise forms.ValidationError('Passwords do not match')

    def register(request):
        if request.method == 'POST':
            form = RegistrationForm(request.POST)
            if form.is_valid():
                email = form.cleaned_data.get('email')
                password = form.cleaned_data.get('password')
                User.objects.create_user(email=email, password=password)
                user = authenticate(email=email, password=password)
                login(request, user)
                return redirect('home')
        else:
            form = RegistrationForm()
        return render(request, 'register.html', {'form': form})

In this example, we have defined a RegistrationForm form that handles input validation for the user’s email and password, and a register view function that displays the form and creates a new user account when the form is submitted. If the registration is successful, the user is logged in and redirected to the home view.

With these steps, you should now be able to use Django’s built-in authentication forms and views to handle user login, logout, and registration in your Django project. In the next section, we will look at how to store hashed passwords in the database to ensure the security of user accounts.

Storing Hashed Passwords in the Database

To ensure the security of user accounts, it is important to store passwords in a secure way in the database. One way to do this is to hash the passwords using a cryptographic hashing function before storing them in the database.

Hashing is a one-way process that takes an input (in this case, a password) and produces a fixed-size output (the hash). Hashing algorithms are designed to be computationally infeasible to reverse, which means that it is extremely difficult to recreate the original password from the hash. This makes it much more difficult for an attacker to compromise user accounts by accessing the hashed passwords in the database.

Django provides a built-in password hashing system that automatically hashes and stores passwords using the bcrypt algorithm when a user is created or their password is changed. To use this system, you will need to set the PASSWORD_HASHERS setting in your settings.py file to include 'django.contrib.auth.hashers.BCryptSHA256PasswordHasher'.

PASSWORD_HASHERS = ['django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
                    'django.contrib.auth.hashers.PBKDF2PasswordHasher',
                    'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
                    'django.contrib.auth.hashers.Argon2PasswordHasher', ]

With this setting in place, Django will automatically hash and store user passwords using the bcrypt algorithm when a user is created or their password is changed.

To check if a user’s password is correct, you can use the check_password method provided by Django’s User model. This method takes a plaintext password and compares it to the hashed password stored in the database. If the passwords match, the method returns True, otherwise it returns False.

from django.contrib.auth.models import User

def check_password(email, password):
    try:
        user = User.objects.get(email=email)
        return user.check_password(password)
    except User.DoesNotExist:
        return False

By hashing and storing passwords in the database, you can help ensure the security of user accounts and protect against password compromise. In the next section, we will look at how to handle login and logout using Django’s built-in auth views.

Handling Login and Logout with Django’s auth Views

Django’s django.contrib.auth package includes a set of built-in views that you can use to handle user login and logout.

To handle user login, you can use the login view provided by django.contrib.auth. The login view displays a login form to the user and verifies their credentials when they submit the form.

To use the login view in a Django template, you can include a login form using the {% url %} template tag and the csrf_token template tag to protect against cross-site request forgery (CSRF) attacks.

<form method="post" action="{% url 'login' %}">
    {% csrf_token %}
    {{ form.as_p }}
    <input type="submit" value="Log in">
</form>

In this example, we have included a login form in a Django template that submits a POST request to the login view when the form is submitted. The csrf_token template tag protects against CSRF attacks by including a hidden input field with a token that is checked by Django’s CSRF middleware.

To handle user logout, you can use the logout view provided by django.contrib.auth. The logout view destroys the user’s session and logs them out of the system.

To use the logout view in a Django template, you can include a logout link using the {% url %} template tag and the csrf_token template tag.

<form method="post" action="{% url 'logout' %}">
    {% csrf_token %}
    <input type="submit" value="Log out">
</form>

In this example, we have included a logout form in a Django template that submits a POST request to the logout view when the form is submitted.

With these steps, you should now be able to handle user login and logout using Django’s built-in auth views in your Django project. In the next section, we will look at how to customize the user authentication flow using Django signals.

Customizing the User Authentication Flow with Django Signals

Django signals allow you to specify certain actions to be taken when certain events occur in your Django application. You can use Django signals to customize the user authentication flow by defining custom signal handlers that are triggered when certain events occur, such as a user logging in or out.

For example, you might want to send a welcome email to a user when they register for an account, or track user activity using a third-party analytics service. To do this, you can define a custom signal handler that is triggered when a user is created, and send the email or track the event from within the signal handler. Here is an example of how you might use Django signals to send a welcome email to a user when they register for an account:

from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.core.mail import send_mail


@receiver(post_save, sender=User)
def send_welcome_email(sender, instance, created, **kwargs):
    if created:
        subject = 'Welcome to My Site!'
        message = 'Thank you for registering with My Site. We hope you enjoy using our service.'
        from_email = 'support@mysite.com'
        send_mail(subject, message, from_email, [instance.email])

In this example, we have defined a custom signal handler using the @receiver decorator that is triggered when a new User object is created. When the signal is triggered, the send_welcome_email function sends a welcome email to the user using Django’s built-in send_mail function.

To use this signal handler in your Django project, you will need to include the import statements at the top of your code and make sure that the Django signals system is configured correctly.

INSTALLED_APPS = ['django.contrib.auth',
                  'django.contrib.contenttypes',
                  'django.contrib.sessions',
                  'django.contrib.messages',
                  'django.contrib.staticfiles',
                  'django.contrib.sites',
                  'django.contrib.sitemaps',    ...]

With these steps, you should now be able to customize the user authentication flow using Django signals in your Django project.

Working with User Permissions and Groups

Django’s django.contrib.auth package includes support for managing user permissions and groups. You can use this support to control which actions users are allowed to perform in your Django application.

To work with user permissions and groups, you will need to create Permission objects for each action that you want to control. Each Permission object is associated with a Django model and a specific action (such as add, change, or delete).

To create Permission objects in your Django project, you can use the Django admin interface or the Permission model from the django.contrib.auth.models module.

Here is an example of how you might create Permission objects using the Permission model:

from django.contrib.auth.models import Permission, Group

# Create a permission to add articles
add_article_permission = Permission.objects.create(
    codename='add_article',
    name='Can add articles',
    content_type=ContentType.objects.get_for_model(Article)
)

# Create a permission to change articles
change_article_permission = Permission.objects.create(
    codename='change_article',
    name='Can change articles',
    content_type=ContentType.objects.get_for_model(Article)
)

# Create a permission to delete articles
delete_article_permission = Permission.objects.create(
    codename='delete_article',
    name='Can delete articles',
    content_type=ContentType.objects.get_for_model(Article)
)

In this example, we have created three Permission objects: one to allow users to add articles, one to allow users to change articles, and one to allow users to delete articles. Each Permission object is associated with the Article model, which means that it can be applied to any instance of the Article model.

Once you have created Permission objects for the actions you want to control, you can assign them to users or groups of users using User or Group objects.

Here is an example of how you might assign permissions to a user:

from django.contrib.auth.models import User

# Get the user
user = User.objects.get(username='john')

# Assign the add permission to the user
user.user_permissions.add(add_article_permission)

# Assign the change permission to the user
user.user_permissions.add(change_article_permission)

# Assign the delete permission to the user
user.user_permissions.add(delete_article_permission)

In this example, we have retrieved a User object for the user with the username 'john' and assigned the add, change, and delete permissions to the user.

You can also create Group objects and assign permissions to them. Users who are members of a group will automatically inherit the permissions assigned to the group.

Here is an example of how you might create a group and assign permissions to it:

# Create a group
group = Group.objects.create(name='Editors')

# Assign the add permission to the group
group.permissions.add(add_article_permission)

# Assign the change permission to the group
group.permissions.add(change_article_permission)

# Assign the delete permission to the group
group.permissions.add(delete_article_permission)

# Add a user to the group
user = User.objects.get(username='john')
group.user_set.add(user)

In this example, we have created a Group object called 'Editors' and assigned the add, change, and delete permissions to the group. We have also added the user 'john' to the group, which means that they will inherit the permissions assigned to the group.

With these steps, you should now be able to work with user permissions and groups in your Django project. You can use these features to control which actions users are allowed to perform, and make it easy to manage permissions for large numbers of users.

Best Practices for Secure User Authentication in Django

Here are some best practices for secure user authentication in Django:

  1. Use Django’s built-in password validation and hashing functions to ensure that passwords are stored securely in the database.
  2. Use Django’s built-in CSRF protection to prevent cross-site request forgery (CSRF) attacks.
  3. Use SSL/TLS to encrypt the connection between the client and the server and prevent interception of sensitive data.
  4. Use Django’s built-in session protection to prevent session hijacking.
  5. Use two-factor authentication (2FA) to provide an additional layer of security for user accounts.
  6. Use Django’s built-in password reset functionality to allow users to reset their passwords if they have forgotten them.
  7. Regularly review and update your authentication and security practices to ensure that they are up to date and effective.

By following these best practices, you can help ensure the security of user accounts in your Django project and protect against common threats such as password compromise and session hijacking.

Sharing is caring 🙂