feat(auth): email+password registration with two-gate activation (5a backend)

Add email+password auth alongside Google: argon2id hashing; users gains
password_hash/email_verified/is_active and google_sub becomes nullable (migration
0022); a single-use, hashed auth_tokens table for email verification + password
reset. Registration creates a pending (inactive, unverified) account + an access
request; sign-in needs verified email AND admin approval. Anti-enumeration: uniform
register/login/reset responses (a correct-password owner still gets a specific
pending reason). allow_registration flag (DB-tunable). Admin users-list + role
endpoint (guards: not self, not demo, not the last admin); approving access (or a
manual whitelist add) now activates the matching pending account. current_user
rejects deactivated accounts. Verified end-to-end via curl + DB.
This commit is contained in:
npeter83 2026-06-19 14:03:11 +02:00
parent 4fc7e1e7df
commit 7efd4f4867
9 changed files with 398 additions and 6 deletions

View file

@ -50,6 +50,8 @@ SPECS: tuple[ConfigSpec, ...] = (
ConfigSpec("autotag_title_sample", "int", "batch", "autotag_title_sample", min=1, max=1_000),
# --- YouTube Data API key (secret; optional — public reads prefer it over OAuth) ---
ConfigSpec("youtube_api_key", "str", "youtube", "youtube_api_key", secret=True),
# --- Access / registration ---
ConfigSpec("allow_registration", "bool", "access", "allow_registration"),
)
_BY_KEY: dict[str, ConfigSpec] = {s.key: s for s in SPECS}