Two-factor authentication with TOTP and recovery codes, through allauth's mfa app #27
Labels
No labels
accessibility
authentication
breaking change
bug
documentation
enhancement
interface
internationalisation
observability
security
tier
1
tier
2
tier
3
tier/4
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Depends on
Reference
Postulo/postulo#27
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Observation
What exists today
INSTALLED_APPSenablesallauthandallauth.account.allauth.mfawith three authenticator types —totp,recovery_codesandwebauthn— plus "trust this browser" and the reauthentication step that guards changes to any of them. Its settings, checked in the installed package:MFA_SUPPORTED_TYPES,MFA_TOTP_ISSUER,MFA_TOTP_DIGITS,MFA_TOTP_PERIOD,MFA_TOTP_TOLERANCE,MFA_RECOVERY_CODE_COUNT,MFA_RECOVERY_CODES_SHOW_ONCE,MFA_TRUST_ENABLED,MFA_TRUST_COOKIE_AGE,MFA_PASSKEY_LOGIN_ENABLED,MFA_WEBAUTHN_ALLOW_INSECURE_ORIGIN,MFA_ADAPTER.mfaextra pullsqrcode(for the enrolment QR code) andfido2(passkeys only). Neither is installed;pyproject.tomldepends ondjango-allauthwith no extras.templates/mfa/overrides exist; theaccount/ones show the pattern to follow.So this is enabling and styling what Postulo already depends on, plus one dependency line. Not a plugin: signing in is core, and it must work with nothing extra installed.
Shape
1. Enable
allauth.mfawithMFA_SUPPORTED_TYPES = ["totp", "recovery_codes"]anddjango-allauth[mfa]inpyproject.toml.qrcoderenders the code as SVG; no image library involved.MFA_TOTP_ISSUERis the instance name once #24 has one, "Postulo" until then, so the entry in the authenticator app is recognisable.2. Where it lives: Settings → Account → Two-factor authentication (#22). Activate: scan the QR code or type the key, confirm with one code, then the recovery codes shown once (
MFA_RECOVERY_CODES_SHOW_ONCE), with Download and I have saved these. Afterwards: regenerate codes, see how many remain, deactivate. Every change asks for the password again first — allauth's reauthentication, which is the right friction.3. Signing in. Password, then the code, with trust this browser for 30 days (
MFA_TRUST_ENABLED,MFA_TRUST_COOKIE_AGE) so a personal machine is not asked daily. The same second step applies after an SSO sign-in (#6): allauth runs MFA on any login, which is what one wants — the identity provider's assurance is not Postulo's to assume.4. What it does not protect, said plainly. Capture tokens and the personal access tokens of #12 are their own credential and bypass the second factor by design — an extension or an agent has no code to type. The wiki says so, and the tokens page says so, so nobody believes TOTP covers the API.
5. Recovery on a self-hosted instance. There is no support desk. Two paths, both needed: the recovery codes, and an administrator resetting a person's second factor from Server settings → People (#24) after satisfying themselves who is asking — plus a management command for the administrator who locks themselves out,
manage.py mfa_reset <email>, so recovery never requires editing the database by hand.6. Policy. A Server settings switch (#24 → Sign-in): require two-factor for administrators and require it for everyone. allauth has no such policy built in; a small middleware that sends a signed-in person without an authenticator to the activation page, once the switch is on, is all it takes. Default off; administrators-only is the sensible first setting on a shared instance.
7. Templates. allauth's
mfa/templates restyled into the shell exactly as theaccount/ones were, including the sign-in code prompt. The QR code is inline SVG, so the CSP is untouched.8. Stage two: passkeys. Same app,
webauthninMFA_SUPPORTED_TYPES,fido2installed. Passkeys are the better factor and can even replace the password (MFA_PASSKEY_LOGIN_ENABLED), but WebAuthn requires a secure origin: the ragnar test instance on plain HTTP inside the mesh could not use them (MFA_WEBAUTHN_ALLOW_INSECURE_ORIGINexists for development only). TOTP first, passkeys as their own step once the shape is in place.9. Documentation. Accounts and invitations gains a two-factor section; Troubleshooting gains "I lost my phone".
Classification
Enhancement. Not breaking: opt-in per person, the policy switch is off by default, and the only operator-visible change is one dependency that the image already carries once merged.
Open questions