skip to content

Accounts & Sign-In

Django's contrib.auth decides who a user is: the User model, authenticate() and login(), password hashers and pluggable backends. Interviewers probe custom user models and session rotation.

part ofDjangooverview, primer and where to startread it →
on this pageshow

explore

questions

26

In Django's contrib.auth, what is the difference between authenticate() and login(), and why does a sign-in view call both?

level: juniorimportance: must knowfreq 76%

answer

  1. checking versus remembering
  2. one returns a user or nothing
  3. the other writes to the session
  4. which backend vouched for the user

basics

~20 s

authenticate() checks credentials against the configured backends and returns a user or None without changing any state. login() takes that user and records it in the session, so later requests arrive with request.user set. A sign-in view needs both steps.

solid answer

~40 s

`authenticate(request, **credentials)` walks `AUTHENTICATION_BACKENDS`, asks each compatible backend to verify the credentials, and returns the first user it gets back, annotated with `user.backend`, or `None` after sending `user_login_failed`. It touches no session and sets no cookie. `login(request, user)` is the stateful step: it rotates or flushes the session, stores the user's primary key, the backend path and a password-derived hash in `request.session`, sets `request.user`, rotates the CSRF token and sends `user_logged_in`. So a view that only authenticates has proved the password but logs nobody in, and a view that calls `login()` on an unverified user object skips the credential check entirely. The built-in `LoginView` does both through `AuthenticationForm`.

code

python · 17 lines
python
from django.contrib.auth import authenticate, login
from django.shortcuts import redirect, render


def member_sign_in(request):
    error = None
    if request.method == 'POST':
        user = authenticate(
            request,
            username=request.POST.get('membership_number'),
            password=request.POST.get('password'),
        )
        if user is not None:
            login(request, user)
            return redirect('class-schedule')
        error = 'Membership number or password is incorrect.'
    return render(request, 'members/sign_in.html', {'error': error})

go deeper

for a junior

Recall the pairing: authenticate() returns a user or None, login() stores that user in the session, and request.user is an AnonymousUser when nobody is signed in.

for a middle

Explain what login() writes to the session, why it needs the backend path, and how AuthenticationMiddleware rebuilds request.user from those keys on the next request.

for a senior

Spot the bypass of calling login() on an unverified user and the ValueError with multiple backends, and know where the is_active check really lives.

for a principal

Use the split deliberately: re-verify credentials for sensitive actions with authenticate() alone, and let recovery flows call login() only after an equivalent proof.

## Two steps, two jobs `django.contrib.auth` splits signing in into **verification** and **persistence**: | Function | Input | Output | Side effects | |---|---|---|---| | `authenticate(request, **credentials)` | credentials such as `username` and `password` | a user object or `None` | none on success; sends `user_login_failed` on failure | | `login(request, user, backend=None)` | a user object already verified | nothing | writes the session, sets `request.user`, rotates the CSRF token, sends `user_logged_in` | Keeping them apart lets you verify a password without logging anyone in (for example to confirm a sensitive action) and lets flows such as password reset log a user in without asking for credentials again. ## What authenticate() does 1. It iterates over `AUTHENTICATION_BACKENDS` in order, skipping any backend whose `authenticate()` signature cannot accept the keyword arguments you passed. 2. Each backend returns a user or `None`. The first user wins; Django sets `user.backend` to that backend's dotted path. 3. A backend may raise `PermissionDenied` to veto the login outright; `authenticate()` catches it, stops trying other backends and falls through to failure. 4. On failure it sends `user_login_failed` with the credentials cleansed of anything that looks like a password or token, and returns `None`. With the default `ModelBackend`, an account whose `is_active` is `False` is rejected here too, so `authenticate()` returns `None` for a deactivated member. ## What login() does `login()` assumes the user is genuine. It: - rotates the session key for an anonymous session with `cycle_key()`, or empties it with `flush()` if it belonged to a different user; - stores three keys: `_auth_user_id` (the primary key as a string), `_auth_user_backend` (the backend path) and `_auth_user_hash` (an HMAC of the password field); - sets `request.user` for the rest of the current request; - rotates the CSRF token and sends `user_logged_in`. The backend path matters. `login()` takes it from the `backend` argument or from `user.backend`, which `authenticate()` set. If neither exists and more than one backend is configured, it raises `ValueError` asking you to pass `backend`. With a single backend it uses that one. On every later request, `AuthenticationMiddleware` makes `request.user` a lazy object that reads those keys, asks the stored backend's `get_user()` for the user and checks the hash; if anything is missing or wrong you get an `AnonymousUser`. ## Putting them together A members-only gym portal's sign-in view verifies first and persists second: ```python user = authenticate(request, username=number, password=password) if user is not None: login(request, user) ``` Most projects never write this by hand: `LoginView` uses `AuthenticationForm`, whose `clean()` calls `authenticate()` and then `confirm_login_allowed()` (which rejects inactive users with an error message), and whose `form_valid()` calls `login()` with `form.get_user()`. ## What the next request sees The split also explains what happens after the response is sent. On the following request `AuthenticationMiddleware` installs a lazy `request.user`; the first access runs `get_user()`, which: - reads `_auth_user_id` and `_auth_user_backend` from the session; - checks that the backend path is still listed in `AUTHENTICATION_BACKENDS`; - calls that backend's `get_user(user_id)`, which for `ModelBackend` also rejects inactive accounts; - compares the stored `_auth_user_hash` with a freshly computed one. Any failure yields `AnonymousUser`, whose `is_authenticated` is `False`, whose `pk` is `None` and whose permission checks come back empty. That is why templates can test `user.is_authenticated` without first checking for `None`. ## Timing and enumeration `ModelBackend.authenticate()` runs the password hasher even when no account matches the username, by hashing the submitted password on a throwaway user instance. The response time is then similar for an unknown membership number and a wrong password, which makes it harder to discover which numbers exist. Custom backends should keep that property. ## Common mistakes - **Calling only `authenticate()`** and expecting the member to be logged in on the next page. Nothing was stored, so the next request sees `AnonymousUser`. - **Calling `login()` on a user fetched by primary key** without verifying anything, which is an authentication bypass if the key came from the request. - **Treating `None` as an exception path.** `authenticate()` does not raise for bad credentials; you must test for `None`. - **Assuming `login()` checks `is_active`.** It does not; the check lives in `ModelBackend` and in `AuthenticationForm`.

  • Why can login() fail with a ValueError when you pass it a user loaded with get()?
    `login()` needs to store which backend vouched for the user. It reads `user.backend`, which only `authenticate()` sets. With several entries in `AUTHENTICATION_BACKENDS` and no `backend=` argument, it cannot choose and raises `ValueError`. Pass `backend='django.contrib.auth.backends.ModelBackend'` explicitly when logging in a user you verified another way, for example right after sign-up.
  • Does login() keep data the visitor put in the session before signing in?
    For an anonymous session, yes: `login()` calls `cycle_key()`, which issues a new session key but keeps the stored data, so a class the visitor picked before signing in survives. If the session already belonged to a different authenticated user, `login()` calls `flush()` instead and the data is discarded.
  • What does request.user return when nobody is logged in?
    An `AnonymousUser` instance, not `None`. It has `is_authenticated` set to `False`, `is_anonymous` set to `True`, `pk` set to `None`, and permission checks that return `False` or empty sets, so templates and views can use the same attributes without checking for `None` first.

saying these in an interview costs you the question

  • authenticate() logs the user in and sets the session cookie
  • authenticate() raises an exception when the password is wrong
  • login() re-checks the password before storing the user
  • login() refuses inactive users on its own
  • request.user is None for visitors who are not signed in
open as a page

In Django, how do you restrict a view to signed-in users with login_required or LoginRequiredMixin, and what happens to an anonymous visitor?

level: juniorimportance: must knowfreq 72%

basics

~10 s

Decorate a function view with @login_required or put LoginRequiredMixin first in a class-based view's bases. An anonymous request is redirected to settings.LOGIN_URL (default /accounts/login/) with ?next= carrying the original path.

open as a page

In Django, when would you build a custom user model on AbstractUser rather than on AbstractBaseUser with PermissionsMixin?

level: middleimportance: must knowfreq 74%

basics

~20 s

Subclass AbstractUser to keep the full default user and add fields. Use AbstractBaseUser when the identity shape differs: it supplies only password and last_login, so you define the fields, USERNAME_FIELD and a manager, adding PermissionsMixin for groups.

open as a page

In Django, how does authenticate() walk the AUTHENTICATION_BACKENDS list, and how does a backend returning None differ from raising PermissionDenied?

level: middleimportance: must knowfreq 52%

basics

~10 s

authenticate() tries each backend in AUTHENTICATION_BACKENDS order and returns the first user found. Returning None passes to the next backend; raising PermissionDenied stops the chain, so authenticate() returns None without trying later backends.

open as a page

How would you write a custom Django authentication backend that lets users sign in with their email address and password?

level: middleimportance: must knowfreq 62%

basics

~10 s

Subclass ModelBackend, override authenticate() to look the user up by email case-insensitively, check_password() it, return the user only if user_can_authenticate() passes, and add the class to AUTHENTICATION_BACKENDS. get_user() and permissions are inherited.

open as a page

How does Django store a user's password, and what role does the order of the PASSWORD_HASHERS setting play?

level: middleimportance: must knowfreq 62%

basics

~10 s

Django stores algorithm$parameters$salt$hash, by default pbkdf2_sha256 with 1,500,000 iterations in 6.1. The first PASSWORD_HASHERS entry hashes new passwords; the others only verify existing hashes, chosen by the stored prefix.

open as a page

In Django, why should code reference settings.AUTH_USER_MODEL or get_user_model() instead of importing the User class directly?

level: juniorimportance: should knowfreq 58%

basics

~10 s

Django's user model is swappable: AUTH_USER_MODEL names whichever model the project uses. Importing auth.User hard-codes the default and breaks once it is swapped, so relations use the setting string and runtime code calls get_user_model().

open as a page

What does django-allauth add on top of Django's contrib.auth, and how do you plug it into a project?

level: juniorimportance: should knowfreq 48%

basics

~10 s

django-allauth adds signup, email verification and management, rate-limited login by username or email, social providers and MFA on top of contrib.auth. Install its apps, its AuthenticationBackend, AccountMiddleware, and include allauth.urls.

open as a page

In Django, what does the default ModelBackend check when authenticate() is called, and why can an inactive user not log in?

level: juniorimportance: should knowfreq 45%

basics

~10 s

ModelBackend, the default AUTHENTICATION_BACKENDS entry, looks the user up by USERNAME_FIELD, verifies the password hash with check_password(), and returns the user only if user_can_authenticate() passes, which rejects is_active=False.

open as a page

Why does a plain link to Django's LogoutView stop working in Django 5.0 and later, and what does logout() actually clear?

level: juniorimportance: should knowfreq 48%

basics

~20 s

Since Django 5.0 LogoutView accepts only POST (and OPTIONS), so a GET link gets 405 and logs nobody out. logout() sends user_logged_out, flushes the current session, deleting its data and key, and sets request.user to AnonymousUser.

open as a page

What do Django's AUTH_PASSWORD_VALIDATORS check in a new project, and which Django code paths actually run them?

level: juniorimportance: should knowfreq 50%

basics

~10 s

A new project lists four validators: attribute similarity, minimum length (8 by default), common passwords and all-numeric. validate_password() runs them from Django's password forms and the createsuperuser and changepassword commands, not from set_password().

open as a page

For a SaaS product where customers sign in by email, how do you define a Django custom user model with USERNAME_FIELD and BaseUserManager?

level: middleimportance: should knowfreq 52%

basics

~10 s

Subclass AbstractBaseUser and PermissionsMixin, declare a unique EmailField, set USERNAME_FIELD = 'email' and REQUIRED_FIELDS for other mandatory fields, and write a BaseUserManager subclass whose create_user and create_superuser take email instead of username.

open as a page

How do you configure django-allauth so community members sign in with their email address and cannot log in until it is verified?

level: middleimportance: should knowfreq 40%

basics

~10 s

Set ACCOUNT_LOGIN_METHODS = {"email"}, ACCOUNT_SIGNUP_FIELDS = ["email*", "password1*", "password2*"] and ACCOUNT_EMAIL_VERIFICATION = "mandatory"; the default "optional" lets unverified users in. Verification uses an HMAC link, or a code when enabled.

open as a page

Why must a custom Django authentication backend implement get_user(), and what does Django store in the session about the backend that logged a user in?

level: middleimportance: should knowfreq 40%

basics

~20 s

login() stores the user's primary key and the backend's dotted path (_auth_user_backend) in the session; on every later request Django reloads that backend and calls its get_user(pk). A backend returning None there leaves the user anonymous.

open as a page

What does Django's login() do to the session it finds, and how does that protect a members-only portal against session fixation?

level: middleimportance: should knowfreq 52%

basics

~20 s

login() cycles the key of an anonymous session, keeping its data, and flushes a session that belonged to another user. A key planted before sign-in therefore never becomes authenticated. It then stores the user id, backend and password hash.

open as a page

How does Django's LoginRequiredMiddleware make an internal tool require login on every page, and how do you keep its health-check endpoint public?

level: middleimportance: should knowfreq 42%

basics

~10 s

LoginRequiredMiddleware (Django 5.1) redirects every unauthenticated request to LOGIN_URL unless the resolved view carries login_not_required. Mark the health check with @login_not_required, or method_decorator on dispatch for a class-based view.

open as a page

How does Django's PasswordResetTokenGenerator make reset links expire and work only once without storing any tokens in the database?

level: middleimportance: should knowfreq 40%

basics

~20 s

The token is a base36 timestamp plus an HMAC of the user's pk, password hash, last_login, that timestamp and email. Changing the password changes the HMAC input, killing the link; check_token() rejects tokens older than PASSWORD_RESET_TIMEOUT (3 days).

open as a page

Walk through Django's built-in password reset flow: which four views run, and why does the confirm view redirect to a set-password URL?

level: middleimportance: should knowfreq 46%

basics

~20 s

PasswordResetView emails a uidb64/token link, PasswordResetDoneView confirms, PasswordResetConfirmView checks the token and sets the new password, PasswordResetCompleteView finishes. The confirm view moves the token into the session and redirects so it stays out of Referer headers.

open as a page

Why must a Django project set AUTH_USER_MODEL before its first migrate, and what does switching to a custom user model on a live database involve?

level: seniorimportance: should knowfreq 46%

basics

~20 s

Every foreign key to the user and every migration that depends on it is recorded against the model active at the first migrate. Switching later needs a hand-built transition: new model on the old table, a manually recorded migration, and data and relation fixes.

open as a page

On a django-allauth community site, someone signs in with a social provider whose email matches an existing local account; what happens, and how do you control it safely?

level: seniorimportance: should knowfreq 32%

basics

~10 s

By default allauth does not log the person into the existing account; auto-signup stops on the email conflict. SOCIALACCOUNT_EMAIL_AUTHENTICATION (globally or per provider) enables matching on provider-verified emails, for fully trusted providers only.

open as a page

Your Django app sits behind a corporate SSO proxy that puts the authenticated username in a request header; how do you wire this up safely?

level: seniorimportance: should knowfreq 28%

basics

~10 s

Add a RemoteUserMiddleware subclass whose header attribute names the proxy's request.META key, after AuthenticationMiddleware, and list RemoteUserBackend in AUTHENTICATION_BACKENDS. The proxy must always strip or overwrite that header, or anyone can impersonate anyone.

open as a page

A Django gym portal's custom password-change view logs the member out right after saving; why, and how does update_session_auth_hash() change what happens to their sessions?

level: seniorimportance: should knowfreq 44%

basics

~20 s

login() stores an HMAC of the password field in the session, and each request recomputes and compares it. A new password changes it, so the session is flushed. update_session_auth_hash() stores the new hash and cycles the key for the current session only.

open as a page

How can the next parameter in a Django login flow become an open redirect, and how does url_has_allowed_host_and_scheme() prevent it?

level: seniorimportance: should knowfreq 45%

basics

~20 s

next is user input, so a view that redirects to it blindly can send victims to an attacker's site after a real login. django.utils.http.url_has_allowed_host_and_scheme() accepts only relative URLs or allowed hosts, with https enforced when required.

open as a page

After importing a legacy user base with salted MD5 hashes into Django, how do you move every account to a strong hasher, including users who never sign in again?

level: seniorimportance: should knowfreq 38%

basics

~20 s

Store the hashes in a format a listed hasher verifies, so Django upgrades each one on a correct login. Dormant accounts never upgrade, so wrap their MD5 hashes in PBKDF2 with a custom hasher and a data migration, then drop MD5.

open as a page

In an async Django view, why should you use await request.auser() and alogin() instead of request.user and login()?

level: middleimportance: nice to knowfreq 30%

basics

~20 s

request.user is a lazy object whose first access reads the session and queries the user synchronously, which Django forbids inside an event loop. request.auser(), alogin(), alogout() and aauthenticate(), added in Django 5.0, do the same work asynchronously.

open as a page

You enable TOTP with django-allauth's allauth.mfa on a community site; how does it hook into login, and which gaps must you still close yourself?

level: seniorimportance: nice to knowfreq 26%

basics

~20 s

allauth.mfa adds a login stage that asks for a TOTP (or WebAuthn) code after credentials for users who enabled it. Still yours: the admin login bypass, a shared cache for replay and rate limits, and encrypting stored secrets.

open as a page