RizTech Academy logo
RizTech Academy
Authentication and PermissionsLesson 2 of 530 min

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

  1. Wire include("django.contrib.auth.urls") and add a registration/login.html; log in through the built-in view.
  2. Set LOGIN_URL, LOGIN_REDIRECT_URL, LOGOUT_REDIRECT_URL; confirm each redirect goes where you set it.
  3. 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.
  4. Use {% if user.is_authenticated %} in a template to show a name or a "log in" link depending on state.
  5. Set up logout as a POST form with {% csrf_token %}; confirm a GET does not log the user out.
  6. Configure the console email backend and run the password-reset flow; read the reset link Django prints.

Official documentation

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