Skip to main content

Implementing Login and Password Reset Flows

This tutorial guides you through implementing a complete authentication system using the built-in views and forms provided by this codebase. You will build a flow that handles user login, secure logout, and a multi-step password reset process.

Prerequisites

To follow this tutorial, ensure your Django project is configured with the following in settings.py:

  • django.contrib.auth and django.contrib.contenttypes in INSTALLED_APPS.
  • LOGIN_REDIRECT_URL: The URL where users are sent after logging in (e.g., '/').
  • LOGOUT_REDIRECT_URL: The URL where users are sent after logging out.
  • A configured email backend (for password reset emails).

Step 1: Configure URL Patterns

The first step is to wire up the authentication views in your urls.py. This codebase provides class-based views that handle the logic for each step.

from django.contrib.auth import views as auth_views
from django.urls import path

urlpatterns = [
# Login and Logout
path("login/", auth_views.LoginView.as_view(), name="login"),
path("logout/", auth_views.LogoutView.as_view(), name="logout"),

# Password Reset Flow
path("password_reset/", auth_views.PasswordResetView.as_view(), name="password_reset"),
path("password_reset/done/", auth_views.PasswordResetDoneView.as_view(), name="password_reset_done"),
path("reset/<uidb64>/<token>/", auth_views.PasswordResetConfirmView.as_view(), name="password_reset_confirm"),
path("reset/done/", auth_views.PasswordResetCompleteView.as_view(), name="password_reset_complete"),
]

By default, these views look for templates in a registration/ directory.

Step 2: Create the Login Template

The LoginView uses the AuthenticationForm by default. This form handles username and password validation. Create a template at registration/login.html:

<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Log In</button>
</form>
<p><a href="{% url 'password_reset' %}">Forgot your password?</a></p>

The AuthenticationForm (found in django/contrib/auth/forms.py) automatically performs a security check via its clean() method, which calls authenticate() to verify credentials and confirm_login_allowed() to ensure the account is active.

Step 3: Handle Secure Logout

In this codebase, LogoutView (in django/contrib/auth/views.py) is restricted to POST requests for security. This prevents accidental logouts from pre-fetching or malicious image tags.

To implement logout, you must use a form in your template:

<form action="{% url 'logout' %}" method="post">
{% csrf_token %}
<button type="submit">Log Out</button>
</form>

When the user clicks this button, LogoutView.post() is called, which executes auth_logout(request) and redirects the user to your LOGOUT_REDIRECT_URL.

Step 4: Implement the Password Reset Flow

The password reset process involves four distinct steps, each managed by a specific view and form.

1. Requesting the Reset

The PasswordResetView uses PasswordResetForm to collect the user's email. When submitted, the form's save() method generates a unique token using PasswordResetTokenGenerator.

2. Sending the Email

The PasswordResetForm.send_mail() method sends an email containing a link with two critical components:

  • uid: The user's ID encoded in base64.
  • token: A secure, one-time-use token generated by PasswordResetTokenGenerator.make_token().

3. Confirming the Reset

When the user clicks the link, they are directed to PasswordResetConfirmView. This view:

  1. Decodes the uidb64 to find the user.
  2. Validates the token using token_generator.check_token().
  3. If valid, displays the SetPasswordForm for the user to enter a new password.

Note: PasswordResetConfirmView performs a transparent redirect to a URL containing set-password to prevent the secret token from leaking in the Referer header of subsequent requests.

4. Completion

Once the password is saved via SetPasswordForm.save(), the user is redirected to PasswordResetCompleteView, which confirms the success and provides a link back to the login page.

Step 5: Password Change for Logged-in Users

For users who are already authenticated and want to change their password, use PasswordChangeView. This view uses PasswordChangeForm, which requires the user to enter their old_password for verification.

# In your urls.py
path("password_change/", auth_views.PasswordChangeView.as_view(), name="password_change"),
path("password_change/done/", auth_views.PasswordChangeDoneView.as_view(), name="password_change_done"),

The PasswordChangeView.form_valid() method calls update_session_auth_hash(self.request, form.user). This is critical because it prevents the user from being logged out of their current session after their password is changed.

Step 6: Customizing User Creation

If you are using a custom user model, you should extend BaseUserCreationForm to ensure proper password hashing and validation.

from django.contrib.auth.forms import BaseUserCreationForm
from myapp.models import MyCustomUser

class CustomUserCreationForm(BaseUserCreationForm):
class Meta(BaseUserCreationForm.Meta):
model = MyCustomUser
fields = ("username", "email", "date_of_birth")

The BaseUserCreationForm (in django/contrib/auth/forms.py) handles the complexity of creating the user instance and calling set_password() correctly during the save() process.

Result

You now have a complete authentication system. Users can:

  1. Log in via LoginView and AuthenticationForm.
  2. Log out securely via a POST request to LogoutView.
  3. Recover their account via the 4-step PasswordReset flow.
  4. Change their password while logged in using PasswordChangeView.

All these components use PasswordResetTokenGenerator and Django's internal validation logic to ensure that tokens are invalidated once used or if the user's security state changes.