minimost.auth
minimost.auth
Authentication routes, password utilities, and access-control decorator.
This module handles everything related to user identity:
Registration (
/signup) — validates and stores new user credentials, creates the user’s SQLite database, and seeds it with public channel history so a new user is not greeted by an empty chat.Login (
/login) — verifies credentials and establishes a Flask session.Logout (
/logout) — marks the user as offline and clears the session.:func:`login_required` — a decorator applied to every route that requires an authenticated session.
Module-level attributes
- AUTH_DBstr
Absolute path to the shared
auth.dbSQLite file that holds all user credentials.- auth_bpflask.Blueprint
The Flask Blueprint for all authentication routes. Registered in
minimost.create_app().- _USERNAME_REre.Pattern
Compiled regular expression that a username must fully match:
[A-Za-z0-9_\-]{1,32}.
- minimost.auth._lockout_settings() tuple[source]
Return
(max_attempts, lockout_seconds)for account lockout.Reads
max_login_attemptsandlockout_duration_minutesfromsettings.jsonon each call, so changes take effect without a restart. Amax_login_attemptsof0(or any non-positive value) disables lockout entirely. Missing, malformed, or invalid values fall back to the built-in defaults (5attempts,15minutes).
- minimost.auth._lockout_message(minutes) str[source]
Return the user-facing error shown while an account is locked out.
- Parameters:
minutes – Whole minutes remaining in the lockout (rounded up; a minimum of
1is always shown).- Returns:
A human-readable lockout message.
- Return type:
- minimost.auth.hash_password(password: str) str[source]
Hash a plaintext password using PBKDF2.
Delegates to
werkzeug.security.generate_password_hash(), which applies a random salt and uses PBKDF2-HMAC-SHA256 by default. The returned string is suitable for storage inauth.dband can be verified withwerkzeug.security.check_password_hash().- Parameters:
password (str) – The plaintext password to hash.
- Returns:
A Werkzeug-format hash string that encodes the algorithm, iterations, salt, and digest.
- Return type:
Example:
hashed = hash_password("S3cr3t!") assert check_password_hash(hashed, "S3cr3t!")
- minimost.auth._seed_channel_history(new_user: str) None[source]
No-op retained for backwards compatibility.
Messages now live in a single shared database, so a newly registered user can already see the full public-channel history the instant their account exists — there is nothing to copy. This stub remains only so existing callers and imports keep working.
- Parameters:
new_user – Ignored.
- Returns:
None
- minimost.auth.login_required(fn)[source]
Decorator that enforces an authenticated Flask session.
Wraps a Flask view function so that unauthenticated requests are redirected to
/logininstead of executing the view. The session key"user"is set by thelogin()route upon successful authentication.This decorator preserves the wrapped function’s name and docstring via
functools.wraps(), which is required for Flask’s endpoint registration to work correctly when multiple routes use the decorator.- Parameters:
fn (callable) – The Flask view function to protect.
- Returns:
A wrapped view function that checks for
session["user"]before calling fn.- Return type:
callable
Example:
@chat_bp.route("/messages/<channel>") @login_required def messages(channel): ...
- minimost.auth.login()[source]
Render the login page.
Routes:
GET /login- Returns:
A rendered
login.htmltemplate.- Return type:
- minimost.auth.about()[source]
Render the “What is MiniMost?” marketing page.
Routes:
GET /aboutA public, login-free page that explains what MiniMost is and pitches visitors on creating an account. Linked from the login page.
- Returns:
A rendered
about.htmltemplate.- Return type:
- minimost.auth.login_post()[source]
Authenticate a user from the login form.
Reads
usernameandpasswordfrom the form, looks up the stored hash inauth.db, and verifies it withwerkzeug.security.check_password_hash().On success:
Sets
session["user"]to the authenticated username.Calls
minimost.common.init_user_db()to ensure the user’s database exists (relevant after a manualusers/directory wipe).Redirects to
/(the main chat interface).
On failure:
Waits 3 seconds before responding to slow down brute-force attempts.
Re-renders
login.htmlwith a generic"Invalid credentials"error (username and password failures are intentionally indistinguishable).
Account lockout: consecutive failed attempts against an existing account are counted in
users.failed_attempts. Once they reachmax_login_attempts(fromsettings.json) the account is locked forlockout_duration_minutesby settingusers.lockout_until; further logins are rejected — without checking the password — until that time passes. A successful login clears the counter. Settingmax_login_attemptsto0disables the feature. See_lockout_settings().Routes:
POST /login- Returns:
A rendered
login.htmltemplate on failure, or a redirect to/on success.- Return type:
- minimost.auth.logout()[source]
Log the current user out and redirect to the login page.
Sets the user’s presence state to
"offline"inpresence.dbviaminimost.presence.update_presence()and clears any manual presence override, then clears the Flask session and redirects to/login.Route:
GET /logout- Returns:
A redirect response to
/login.- Return type:
- minimost.auth._validate_password(password: str)[source]
Return an error string if password fails the strength rules, else
None.Shared by signup and password reset so the rules stay in one place.
- Parameters:
password – The submitted password.
- Returns:
A human-readable error message, or
Noneif all rules pass.- Return type:
str or None
- minimost.auth._validate_signup(username: str, password: str, confirm: str)[source]
Validate signup form fields and return an error string, or
Noneon success.- Parameters:
username – The submitted username.
password – The submitted password.
confirm – The password confirmation field.
- Returns:
A human-readable error message, or
Noneif all rules pass.- Return type:
str or None
- minimost.auth.forgot_password()[source]
Render the forgot-password information page.
Route:
GET /forgot-password- Returns:
A rendered
forgot_password.htmltemplate.- Return type:
- minimost.auth.signup()[source]
Render the registration page.
Route:
GET /signup- Returns:
A rendered
signup.htmltemplate.- Return type:
- minimost.auth.signup_post()[source]
Create a new user account from the registration form.
Validates the submitted
username,password, andconfirm_passwordfields, creates the account, and logs the user in.Validation rules (enforced server-side):
usernamemust fully match[A-Za-z0-9_\-]{1,32}.passwordmust be at least 8 characters long.passwordmust contain at least one digit (\d).passwordmust contain at least one uppercase ASCII letter.passwordmust contain at least one special character from the set!@#$%^&*()_+-=[]{};\':|,./<>?`~.passwordandconfirm_passwordmust match.
On success:
Inserts
(username, hashed_password)intoauth.db.Calls
minimost.common.init_user_db()to create the user’s DB.Calls
_seed_channel_history()to populate public channel history.Calls
minimost.chat.post_welcome_message()to greet the new user in the first public channel under the MiniMost identity.Sets
session["user"]and redirects to/.
On failure:
Returns
signup.htmlwith a descriptiveerrorvariable.If the username is already taken (
IntegrityError), the error message says so.
Route:
POST /signup- Returns:
A rendered
signup.htmltemplate on validation failure, or a redirect to/on successful registration.- Return type:
- minimost.auth._validate_password_reset(password: str, confirm: str)[source]
Return an error string if the new password fails validation, else None.
- minimost.auth.change_password_post()[source]
Change the logged-in user’s password from the account modal.
Reads
current_password,new_password, andconfirm_passwordfrom the submitted form. The current password is verified against the stored hash before the new password is applied; the new password is held to the same strength rules as signup and reset (see_validate_password()).Unlike the HTML form routes, this endpoint is called via
fetchfrom the chat UI and responds with JSON so the modal can show inline feedback without a page reload. CSRF is still enforced (auth blueprint) so the request must include thecsrf_tokenform field.Route:
POST /change-password
- minimost.auth.reset_password_form(token)[source]
Render the password reset form if the token is valid and unexpired.
Route:
GET /reset-password/<token>- Returns:
Rendered
reset_password.htmlwith the form or an error.- Return type:
- minimost.auth.reset_password_post(token)[source]
Process a password reset form submission.
Validates the token is still active, applies password rules, updates the stored hash in
auth.db, and marks the token as used.Route:
POST /reset-password/<token>- Returns:
Rendered
reset_password.html(success or error).- Return type:
Route Summary
Method |
Path |
Handler |
|---|---|---|
GET, POST |
|
|
GET |
|
|
GET, POST |
|