Architecture¶
django-multifactor is a small package. Most of the moving parts are direct
Django primitives (views, models, sessions, the messages framework) plus two
external libraries: pyotp for TOTP and fido2 for WebAuthn. This page is the
mental model you need before any of the other pages make sense.
Component overview¶
flowchart LR
subgraph YourApp["Your Django app"]
YourViews["Your views"]
YourDeco["@multifactor_protected"]
end
subgraph Package["multifactor package"]
direction TB
Decorators["decorators.py<br/>multifactor_protected"]
Mixins["mixins.py<br/>RequireMultiAuthMixin<br/>PreferMultiAuthMixin"]
Common["common.py<br/>has_multifactor()<br/>active_factors()<br/>write_session()<br/>is_bypassed()"]
Views["views.py<br/>List, Add, Authenticate, Help"]
subgraph Factors["factors/"]
FIDO2["fido2.py<br/>Register, Authenticate"]
TOTP["totp.py<br/>Create, Auth"]
Fallback["fallback.py<br/>Auth + transports"]
end
Models["models.py<br/>UserKey, DisabledFallback"]
Templates["templates/<br/>multifactor/*.html<br/>brand.html, email.html (overridable)"]
Settings["app_settings.py<br/>MULTIFACTOR defaults"]
end
subgraph Django["Django core"]
Session[("django.contrib.sessions")]
DB[("Database<br/>(your DEFAULT)")]
Messages["django.contrib.messages"]
end
YourViews --> YourDeco
YourDeco --> Decorators
Decorators --> Common
Mixins --> Common
Common --> Models
Common --> Session
Common --> Settings
Views --> Common
Views --> Factors
FIDO2 --> Models
TOTP --> Models
Fallback --> Messages
Fallback --> Settings
Models --> DB
Views --> Templates
style YourApp fill:#cfe2ff
style Package fill:#fff3cd
style Django fill:#d4edda
The user-facing entry point is the decorator (or one of the mixins). It
inspects the session via common.py, looks up the user’s UserKey rows, and
either:
Lets the request through (
baulk()— i.e. call the wrapped view), orRedirects to
multifactor:authenticate, where the user picks a factor and one of the factor views handles the challenge.
On successful verification the factor view calls common.write_session(),
which records (key_type, key_id, timestamp, recheck_expiry) in the session
and bounces the user back to wherever they were heading.
Where the state lives¶
There are three places django-multifactor keeps state:
Location |
What’s there |
Lifecycle |
|---|---|---|
Database |
|
Persistent. Survives logout. |
Session |
|
Tied to the session. Cleared on logout if your |
Settings |
Everything in |
Immutable at runtime. |
Warning
The session is the source of truth for “is this user currently MFA-verified?”. That means session security is MFA security — see security best practices for cookie hardening.
The two stories: protecting a view vs. registering a factor¶
Two flows dominate the codebase:
Story 1 — a user hits a protected view¶
Request comes in for
billing().@multifactor_protectedruns its checks (auth? bypass? user filter? key count? max age?).If everything is satisfied, the wrapped view runs.
If something fails, the user is bounced to
multifactor:authenticatewithsession["multifactor-next"]set to the original URL.
Story 2 — a user registers a new factor¶
User visits
/admin/multifactor/(views.List).Picks Add factor →
views.Add.Selects FIDO2 or TOTP. The picker mounts the matching template, which talks to either
factors.fido2.Registerorfactors.totp.Createvia XHR/POST.On success a new
UserKeyrow is created andwrite_session()marks the factor as currently active in this session.
For a step-by-step view of either story, see Authentication flow.
What django-multifactor does NOT do¶
It is not a primary authentication system. Users must already be authenticated by Django’s normal
login()before any MFA happens.It does not rate-limit OTP attempts itself. Put a generic rate limiter (django-ratelimit, django-axes, your CDN) in front of
multifactor:*URLs.It does not notify users of new factor registrations. If you want a “your account just had a new security key added” email, hook
signals.post_saveonUserKey.
Where next?¶
See the full request flow: Authentication flow.
Understand session state and recheck: Session model.
Compare the factor types: Factors overview.