Skip to main content

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
Password recovery and email confirmation are off by default because they need a mail server. Turn them on in enferno/settings.py (SECURITY_RECOVERABLE, SECURITY_CONFIRMABLE) and set the mail variables below.

Configuration

Security behavior lives in enferno/settings.py. .env holds the secrets, which setup.sh generates:
The mail defaults are port 465 with SSL on and TLS off. For a provider that uses STARTTLS, set 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

Register these callback URLs with each provider:
  • Google: https://your-domain.com/login/google/authorized
  • GitHub: https://your-domain.com/login/github/authorized
Google requests the profile and email scopes, GitHub user:email. For local testing over plain HTTP, set OAUTHLIB_INSECURE_TRANSPORT=1.

Protecting Routes

Protect a whole blueprint in before_request instead of decorating each route:
For a single route, @roles_accepted("editor", "admin") allows any of the listed roles.

Users and Roles

Admins can also manage users and roles at /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 with setup.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 is POST only, so use a form posting to /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.