Overview
Authentication is built on Flask-Security-Too 5.9. Out of the box you get:- Email login and registration
- Password change, with a 12 character minimum
- TOTP two-factor authentication with recovery codes
- Passkeys (WebAuthn), as a first factor or a second one
- Google and GitHub OAuth
- Roles, with an admin UI for users and roles
- Server-side sessions and CSRF protection on every POST, PUT and DELETE
enferno/settings.py (SECURITY_RECOVERABLE, SECURITY_CONFIRMABLE) and set the mail variables below.
Configuration
Security behavior lives inenferno/settings.py. .env holds the secrets, which setup.sh generates:
MAIL_PORT=587, MAIL_USE_SSL=False and MAIL_USE_TLS=True.
Rerunning setup.sh keeps existing secrets, so sessions, password hashes and 2FA keep working.
OAuth
- Google:
https://your-domain.com/login/google/authorized - GitHub:
https://your-domain.com/login/github/authorized
user:email. For local testing over plain HTTP, set OAUTHLIB_INSECURE_TRANSPORT=1.
Protecting Routes
Protect a whole blueprint inbefore_request instead of decorating each route:
@roles_accepted("editor", "admin") allows any of the listed roles.
Users and Roles
/users/ and /roles/. Every change is recorded in the activity log at /activities/.
Sessions
Sessions are stored server-side, in the database by default or in Redis withsetup.sh --full. They last one hour, and Flask-Security asks for the password again before sensitive changes if the login is older than 60 minutes.
Cookie flags come from .env:
API Requests
The JSON API uses the same session as the pages (SECURITY_API_ENABLED_METHODS = ["session"]). Pages that extend layout.html send the CSRF token with every axios request. Plain HTML forms need:
/logout, never a link.
Troubleshooting
- 2FA codes rejected: TOTP depends on the clock. Check the server and phone time are in sync.
- OAuth redirect mismatch: the callback URL registered with the provider must match the domain and scheme exactly.