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.authanddjango.contrib.contenttypesinINSTALLED_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 byPasswordResetTokenGenerator.make_token().
3. Confirming the Reset
When the user clicks the link, they are directed to PasswordResetConfirmView. This view:
- Decodes the
uidb64to find the user. - Validates the token using
token_generator.check_token(). - If valid, displays the
SetPasswordFormfor 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:
- Log in via
LoginViewandAuthenticationForm. - Log out securely via a
POSTrequest toLogoutView. - Recover their account via the 4-step
PasswordResetflow. - 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.