Django / Reliable application workflows / Lesson 9
Authentication and session lifecycle
Use the configured authentication system and session workflow without confusing a signed-in identity with permission for every resource.
On this page
Lesson goals
- Identity is supplied through a configured backend
- Verify a password without storing a user
- Test continuity, logout, and deployment assumptions
Identity is supplied through a configured backend
Django’s authentication system coordinates user retrieval, password handling, login state, and permissions through configured components. The selected user model can differ from the default, so obtain it through the documented configuration and APIs rather than assuming one class forever. Authentication middleware normally depends on session middleware appearing before it. A request created directly with RequestFactory does not acquire that identity automatically. Distinguish a missing identity, an inactive account under the selected backend, and a known user lacking permission. A successful login does not grant ownership of every Course or permission to change another account’s data.
Passwords require the framework’s storage contract
Use the configured password-hashing APIs or user-management methods rather than storing plaintext or comparing a raw submitted string with a stored hash. Hashing the submitted password anew and comparing the two encoded strings is also incorrect because salts can differ. The first example uses synthetic text to demonstrate make_password and check_password locally. It prints only Boolean verification results, not the encoded hash or a real credential. Password validation policy, account recovery, throttling, and multi-factor integration remain application concerns. Django’s core authentication backend does not automatically supply a complete brute-force rate-limiting policy for a public login endpoint.
Verify a password without storing a user
The first complete program configures its own isolated settings, creates one framework hash from a clearly synthetic password, and checks correct and incorrect candidates. No user table or authentication session is created. The test proves hashing API behavior under the selected runtime, not that a login view, cookie, or reverse proxy is correctly wired. A real login flow should use the chosen authentication form or authenticate API under the selected backend, communicate failures without unnecessary account disclosure, and keep passwords out of logs, query strings, and flashed values. Test the actual credential path separately from tests that force a user identity.
from django.conf import settings
settings.configure(
SECRET_KEY="synthetic-course-key",
USE_I18N=False, USE_TZ=True,
INSTALLED_APPS=[],
DATABASES={"default": {"ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:"}},
DEFAULT_AUTO_FIELD="django.db.models.AutoField",
ALLOWED_HOSTS=["testserver"],
)
import django
django.setup()
from django.contrib.auth.hashers import make_password, check_password
encoded = make_password("synthetic-course-passphrase")
print(check_password("synthetic-course-passphrase", encoded))
print(check_password("different-synthetic-value", encoded))
True
False
Only synthetic candidates and Boolean results are used. No user model, session, or database operation is involved.
Use the maintained login and logout views deliberately
The second fragment wires Django’s LoginView and LogoutView into an application URLconf. It requires the selected auth/session apps, migrations in an isolated training environment, middleware, a real registration/login.html template with CSRF handling, and an intentional post-login policy. In Django 5.2 the built-in logout view is a POST workflow, so a state-changing GET link is not the appropriate control. Use an internal protected form or a reviewed equivalent. Built-in views provide useful lifecycle behavior, but they do not configure every deployment cookie, account recovery route, or custom rate limiter simply because the patterns exist.
from django.contrib.auth.views import LoginView, LogoutView
from django.urls import path
urlpatterns = [
path("accounts/login/", LoginView.as_view(template_name="registration/login.html"), name="login"),
path("accounts/logout/", LogoutView.as_view(next_page="/"), name="logout"),
]
In the declared app: login uses the supplied template; POST logout follows the configured redirect.
Requires the configured authentication/session stack, an actual CSRF-enabled login template, and reviewed supporting policies. Logout is submitted by POST in the Django 5.2 baseline; the fragment is not executed.
Test continuity, logout, and deployment assumptions
Verify valid and invalid credentials, the next protected request, logout, and access after logout. force_login is useful for downstream authenticated tests but bypasses password verification. Cookie domain, secure flags, same-site behavior, HTTPS, and trusted proxy interpretation can explain why a session appears to vanish between requests. Diagnose those facts rather than disabling CSRF or putting credentials in a URL. A signed-cookie session protects integrity but is not an encrypted place for secrets; choose storage and exposed content deliberately. Account-wide session invalidation and external identity providers require their own explicit contracts beyond one browser logout.
Common errors and fixes
A request lacks user or session state
- Why it happens
- The middleware stack or test boundary does not provide it.
- How to fix it
- Verify middleware order and the intended request path.
- Prevent it next time
- Separate direct view tests from full authentication tests.
Matching passwords fail when hashes are compared directly
- Why it happens
- New hashes can use different salts.
- How to fix it
- Use check_password or the authentication backend.
- Prevent it next time
- Never implement raw encoded-string comparison.
A hidden logout link is assumed to end all sessions
- Why it happens
- The session scope was never defined.
- How to fix it
- Specify browser, device, and external-provider behavior.
- Prevent it next time
- Test the subsequent protected request.
Check your understanding
1. Why can force_login not prove that a login form verifies the password correctly?
Show worked solution
It supplies an authenticated test identity without performing the submitted-credential path. Keep it for downstream permissions or view behavior, then add separate isolated login requests with valid and invalid synthetic credentials and inspect session continuity and failure feedback.
2. Should sensitive application data be placed in a signed-cookie session because it is signed?
Show worked solution
No. Signing protects integrity against undetected alteration; it does not by itself hide the content from the browser holder. Minimize session data and choose a suitable storage and confidentiality policy. Keep actual secrets outside client-delivered state regardless of whether a signature is present.
Key takeaways
- Use the configured user and backend contract.
- Hash verification differs from string equality.
- Session continuity and scope need explicit tests.