# SPEC — Users, Access Matrix, registration, sessions and security **Date:** 2026-09-19 **Status:** hardened Users + Access Matrix; **SMTP configurado e validado (2026-09-19)**. **Module:** `backend/app/modules/users` · Admin UI `/users` · `/users/access` ## Design references (not a framework swap) Patterns adapted from production FastAPI auth libraries (kept in-house RBAC matrix): - [fastapi-fullauth](https://github.com/mdfarhankc/fastapi-fullauth) — lockout, hashed reset tokens, email verification/reset, RBAC permissions, anti-enumeration - [AuthWarden](https://github.com/timihack/authwarden) — single-use reset tokens, OTP attempt limits, brute-force lockout - OWASP Authentication / Session / Password-reset cheat sheets RentCharge keeps its own SQLite models, cookie sessions and **permissions-first Access Matrix** (`module.view.action`). ## Functional scope (both sides) ### A — Self-service (`/login`, `/signup`, `/reset-password`, `/account`) 1. Public registration with strong password + email OTP verification. 2. Forgot-password → single-use email token (SHA-256 stored, 1 hour). 3. Account profile, email verification, password change with strength meter. 4. Forced password change when `must_change_password` is set. ### B — Admin Users + Access Matrix (`/users`, `/users/access`) 1. Create staff logins with **generated strong passwords** (not shared starter defaults). 2. Edit login (username, nickname, display name, recovery email). 3. Set password (modal + strength meter) **or** email a reset token. 4. Require password change on first login / after admin reset. 5. Role assignment + full Role × Permission matrix with dirty-state and default-drift highlight. 6. Sessions revoke + security activity audit. ## Security controls - **Password rules are chosen by Admin** under `/users/password-policy` (not hard-coded). Presets: PIN 6+ digits · alphanumeric · strong · complex · custom toggles (uppercase / lowercase / digit / special / min length / block nick-email / block common). - Passwords: bcrypt cost 12; validation always follows the saved admin policy. - Account lockout after 5 consecutive failed logins (15 minutes) + IP/identity rate limits. - Reset tokens: 256-bit random, stored as SHA-256 only, single-use, invalidate previous tokens. - Registration OTP: HMAC-SHA256, 10 min, max 5 attempts. - Sessions: JWT + server-side session id; HttpOnly Secure SameSite=Strict cookie; idle + absolute timeouts. - Logout / password change / reset / disable revoke sessions. - Generic public responses to reduce account enumeration. - Passwords and raw tokens never written to `auth_events`. ## New / updated API | Method | Path | Purpose | |--------|------|---------| | GET | `/api/auth/password-policy` | Policy hint for UIs | | POST | `/api/auth/password-strength` | Strength score helper | | POST | `/api/users/{id}/send-password-reset` | Admin emails recovery token | | GET | `/api/users/generate-password` | Cryptographically strong starter | | PUT | `/api/users/{id}` | Now accepts `username` (edit login) | | POST | `/api/users` | `must_change_password`, `send_invite_email` | | POST | `/api/users/{id}/set-password` | `must_change_password` flag | ## Database (additive) `users` columns: - `must_change_password` - `failed_login_count` - `locked_until` ## Mail transport (VM500) **Validated 2026-09-19** — OTP verification and password-reset emails delivered. | Setting | Value | |---------|--------| | Host | `mail.myvexx.com` | | Port | `465` (SMTP SSL) | | User | `admin@myvexx.com` | | From | `Rent & Charge ` | | Config | `/home/mini/rentcharge/config/rentcharge.env` (symlink `/etc/rentcharge.env`) | | Also | Postfix on VM500 relays `[mail.myvexx.com]:465` with SASL | App flags: `SMTP_SSL=true`, `SMTP_STARTTLS=false`. Credentials stay only in env (never in Git). E2E checks: `POST /api/auth/forgot-password` → HTTP 200; inbox received **Rent & Charge password reset** and **Your Rent & Charge verification code**. ## Verification checklist - [ ] Weak passwords rejected on register / change / admin set - [ ] Generate strong password fills create form - [ ] Edit login updates username + email - [x] Email reset from Users works when SMTP configured - [ ] Forced change redirects to `/account` - [ ] Access matrix dirty badge + amber default-drift cells - [ ] Backend tests: `python -m unittest backend.tests.test_auth_users`