Authentication
🔐 Authentication
AuthenticationMiddleware has been sitting in MIDDLEWARE since Chapter 1 of the last course — active the whole time, doing nothing until you actually use it. Where FastAPI and Express typically reach for a JWT library and build the login flow by hand, Django's django.contrib.auth ships a complete, battle-tested authentication system: a user model, password hashing, session-based login, and access-control decorators, all included.
The Built-in User Model
Django's default User model already has the fields most apps need — username, email, and a password field that is always stored hashed, never in plain text:
create_user(), Not create()
Bypassing this and using the plain ORM .create() (or a ModelForm without care) would store the raw password unhashed — always go through create_user() or set_password().
Custom User Models
For a production app, swapping in a custom AUTH_USER_MODEL from day one (even one that just extends the default) is standard advice — changing it later requires a full migration rebuild.
Password Hashing, Handled For You
The Authentication & Session Security course covered bcrypt/scrypt/Argon2id and why passwords must never be stored in plain text. Django's default hasher (PBKDF2) implements that guidance automatically — no manual hashing call required anywhere in your own code:
Switching to Argon2id (the algorithm the security course recommended as the strongest modern default) is a one-line settings change, not a rewrite:
🍪 Session-Based Login
Where a typical FastAPI/Express API issues a JWT the client stores and resends, Django's default is session-based — a session ID cookie the server looks up against server-side session data:
FastAPI/Express JWT Flow vs Django Session Flow
Typical FastAPI/Express JWT
Verify credentials → sign a JWT yourself → client stores it (localStorage or a cookie) → client resends it → you verify the signature on every request.
Django Session
login() stores the user's ID server-side and sets a session cookie — AuthenticationMiddleware looks it up and attaches request.user automatically on every subsequent request.
Notice login() does something the Session Security course insisted on doing manually: it rotates the session key on every successful login, preventing session fixation by design — one call, not a step you have to remember.
Protecting Views
Once login() has run, every later request in that session has request.user populated — @login_required is the decorator that turns "must be logged in" into one line:
request.user.is_authenticated
A boolean, true for a logged-in user and false for AnonymousUser — always present, never None, so no null check is needed before reading it.
@permission_required
A step further than @login_required — checks for a specific permission ("catalog.add_book") rather than just "logged in at all."
💻 Coding Challenges
Challenge 1: Write a Registration View
Write a register view that reads username, email, and password from a POST request, creates the user with User.objects.create_user(), and logs them in immediately afterward.
Goal: Practice using create_user() (never a raw ORM .create()) for anything that touches passwords.
Challenge 2: Protect a View
Add @login_required to an existing book_create view, and explain what happens (in terms of the actual HTTP response) when an anonymous user requests that URL.
Goal: Practice the decorator-based access-control pattern and understand its default redirect behavior.
Challenge 3: Switch to Argon2
Write the PASSWORD_HASHERS setting change needed to make Argon2id the primary hashing algorithm for new passwords, while keeping existing PBKDF2-hashed passwords still able to log in.
Goal: Practice a settings-only security upgrade, and explain why the old hasher must stay in the list rather than being removed outright.
It's tempting, especially coming from a FastAPI project where you built JWT auth by hand, to reach for the same pattern in Django — rolling a custom password check and a hand-written session cookie. Resist it: django.contrib.auth already handles password hashing correctly, session fixation prevention, and secure cookie flags, all reviewed by a large security-conscious community over many years. The Authentication & Session Security course's whole thesis — that auth is easy to get subtly wrong — is exactly why using the framework's built-in system, rather than reimplementing it, is the safer default here.
🎯 What's Next
With login working, the next chapter turns to building actual APIs on top of these models: Django REST Framework — serializers, viewsets, and how DRF's approach to building an API compares to a plain Django view returning JsonResponse.