Login, logout and password reset
Authentication — proving who a user is — is something Django provides almost entirely, and writing it yourself is both unnecessary and dangerous (auth is exactly the code you do not want to get subtly wrong). This lesson wires up login, logout and password reset using Django's built-in views and functions, and explains the pieces so you can configure them for Nidaan rather than reinvent them.
The functions underneath: authenticate and login
At the core are two functions. authenticate checks a username and password; login establishes the
session:
from django.contrib.auth import authenticate, login
user = authenticate(request, username="staff1", password="clinic-pass-123")
if user is not None: # verified: returns the user for correct credentials, None for wrong
login(request, user) # establishes the logged-in session
Verified: authenticate returns the user object for correct credentials and None for wrong ones — and it
checks against the hashed password (passwords are stored as pbkdf2_sha256$…, never plaintext), so you
never compare passwords yourself. login(request, user) stores the user's id in the session so subsequent
requests know who they are (via the middleware that sets request.user). You rarely call these directly,
because the built-in views do it for you — but knowing they exist demystifies what the views are doing.
The built-in auth views — do not write your own
django.contrib.auth ships complete, secure views for the whole login lifecycle. Wire them up by including
the auth URLs:
# config/urls.py
urlpatterns = [
path("accounts/", include("django.contrib.auth.urls")),
# ...
]
This one line gives you, under /accounts/, working URLs for: login, logout, password_change,
password_reset and the reset confirmation flow. You provide the templates (Django looks for
registration/login.html, etc.), and the views handle the logic — validating credentials, managing the
session, generating secure reset tokens. Do not hand-roll these. Login and especially password reset
involve security details (timing-safe comparison, signed one-time tokens, session fixation protection) that
are easy to get wrong and catastrophic when you do. Django's versions are correct; use them.
A minimal registration/login.html:
{% extends "base.html" %}
{% block content %}
<form method="post">{% csrf_token %}{{ form.as_p }}<button>Log in</button></form>
{% endblock %}
The login settings
Three settings control where users go:
LOGIN_URL = "login" # where @login_required sends anonymous users
LOGIN_REDIRECT_URL = "patient_list" # where login sends users afterwards
LOGOUT_REDIRECT_URL = "login" # where logout sends them
LOGIN_URL is where an unauthenticated user is redirected when they hit a protected page (default
/accounts/login/). LOGIN_REDIRECT_URL is where a successful login lands them. Set these to sensible
destinations for your app — for Nidaan, login lands staff on the patient list.
Protecting a view: login_required
The other half of auth is requiring it. The login_required decorator sends anonymous users to the login
page:
from django.contrib.auth.decorators import login_required
@login_required
def dashboard(request):
return render(request, "dashboard.html")
Verified: an anonymous request to a @login_required view returns 302, redirecting to the login URL;
once logged in, the same request returns 200. The decorator also appends ?next=… so that after logging
in, the user returns to the page they were trying to reach — a small courtesy Django handles for you. For
class-based views, the equivalent is the LoginRequiredMixin:
from django.contrib.auth.mixins import LoginRequiredMixin
class DashboardView(LoginRequiredMixin, ListView):
...
request.user is always available (courtesy of middleware): it is the logged-in User, or an
AnonymousUser if nobody is logged in. Check request.user.is_authenticated — verified, this is True for
a real user and False for AnonymousUser — to branch on login state in a view or template
({% if user.is_authenticated %}).
Logout, and templates for the flow
Logging out ends the session:
# via the built-in view (POST to /accounts/logout/), or:
from django.contrib.auth import logout
def sign_out(request):
logout(request) # clears the session
return redirect("login")
Note that logout should be a POST, not a GET — a plain link that logs users out can be triggered by a
malicious page (or a prefetcher), so Django's logout view requires POST. Wrap it in a small form with
{% csrf_token %} and a button. Password reset needs a few templates (the reset form, the emailed link, the
new-password form) — Django provides the views and the token security; you provide the templates and,
in production, an email backend to actually send the reset email (development can print it to the console).
Check your work
The core functions. authenticate(request, username, password) returns the user (verified) or None,
checking the hashed password; login(request, user) establishes the session. You rarely call them
directly.
The built-in views. include("django.contrib.auth.urls") gives login/logout/password-change/
password-reset under /accounts/; you provide templates, Django provides the (security-critical) logic.
Never hand-roll them.
The login settings. LOGIN_URL (where anonymous users are sent), LOGIN_REDIRECT_URL (where login
lands them), LOGOUT_REDIRECT_URL.
Protecting views. @login_required (function views) / LoginRequiredMixin (CBVs) redirect anonymous
users to login with ?next= — verified 302 → login when anonymous, 200 when authenticated.
request.user. Always present — the User or AnonymousUser; check is_authenticated (verified True
vs False) to branch on login state.
Logout is POST. Logout should require POST (a GET logout link is a vulnerability); use the built-in view
or logout(request) behind a form.
Practice
- Wire
include("django.contrib.auth.urls")and add aregistration/login.html; log in through the built-in view. - Set
LOGIN_URL,LOGIN_REDIRECT_URL,LOGOUT_REDIRECT_URL; confirm each redirect goes where you set it. - Decorate a view with
@login_required; confirm an anonymous visit redirects to login (302) and a logged-in visit returns 200. Note the?next=parameter. - Use
{% if user.is_authenticated %}in a template to show a name or a "log in" link depending on state. - Set up logout as a POST form with
{% csrf_token %}; confirm a GET does not log the user out. - Configure the console email backend and run the password-reset flow; read the reset link Django prints.
Official documentation
- Django — Using the authentication system —
authenticate,login,logout,login_required. - Django — Authentication views — The built-in login/logout/password views and their templates.
- Django —
LoginRequiredMixin— Protecting class-based views.
Next: permissions and access control.
Stuck on this lesson?
Being stuck is part of it — but being stuck alone for three days is not. Our internship programme pairs this curriculum with code review and one-to-one help from working developers, and it is free.
About the internship