**COLLECTING BRANCH** Auth, sessions, and CSRF protection (Effort 1) #63
No reviewers
Labels
No labels
Compat/Breaking
Kind/Bug
Kind/Documentation
Kind/Enhancement
Kind/Feature
Kind/Security
Kind/Testing
Priority
Critical
Priority
High
Priority
Low
Priority
Medium
Reviewed
Confirmed
Reviewed
Duplicate
Reviewed
Invalid
Reviewed
Won't Fix
Status
Abandoned
Status
Blocked
Status
Need More Info
No milestone
No assignees
1 participant
Notifications
Due date
No due date set.
Reference
marvin8/yunjin!63
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/effort1-auth"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Targets the collecting branch
feat/multiuser(notmain). Effort 1 of 3 of Phase 6.Refs #25
What this does
Adds authentication, sessions, and CSRF protection — the door lock for the reader:
20261002_add_user_credentials):users.password_hash(nullable) andusers.sessions_valid_after(nullable, session-revocation stamp). Idempotent guarded DDL, replay-safe.services/auth.py: werkzeug scrypt hashing (hash_password/verify_password),setup_required,is_session_valid(constant-work verify;>=boundary semantics).web/auth.py: env config (YUNJIN_SESSION_LIFETIME_DAYSdefault 30,YUNJIN_SESSION_COOKIE_SECURE,SESSION_COOKIE_SAMESITE=Lax),current_user/require_current_user, CSRF primitives (secrets.token_hex(32)per session,hmac.compare_digest), and onebefore_requestguard: exempt endpoints → setup gate → login gate → revocation → CSRF on POST/PUT/PATCH/DELETE.authblueprint: first-run/setup(reachable only while no user has a credential; self-disables),/login(full session rotation, permanent 30-day session, open-redirect-safenext),/logout. Constant-work failed-login handling with equalized 0.5 s delay on both wrong-password and unknown-user paths (CWE-204).user_id = 1replaced with the session-derived user (reader, settings, ignore rules, display-settings injection).yunjin set-password <username>CLI: getpass-prompted, migrates the DB first, revokes existing sessions — the standing bootstrap/reset tool.docs/cli.md,docs/config.md.Upgrade notes for existing deployments
After upgrade, the first browser request redirects to
/setup— set the password for the seededdefaultuser there (oruv run yunjin set-password defaultvia docker exec). Existing read state is untouched. SetYUNJIN_SESSION_COOKIE_SECURE=truewhen behind the HTTPS reverse proxy.Evidence
uv run noxall sessions green (ty, ruff, complexipy, pysentry, pytest 3.12/3.13/3.14, deptry, zaojun).b7c618a), all findings verified addressed by scoped re-review.Known deferred minors (tracked for Effort 2/3 or final triage)
db/INSERTs (predates branch)password_hash is NULLlogin path still redirects to/setupwithout delay (bootstrap-only window)set-passwordfresh-DB bootstrap (manual repro only)csrf_tokenis a callable Jinja global (comment added at the call site)CI status note (agent)
The
pull_requestevent dispatch has been wedged forge-side since thebdd5fabpush — two pushes (the fix and a re-trigger empty commit) produced no run rows at all, whileworkflow_dispatchdispatches fine. Same class of outage as the earlier re-trigger commits (764,760) in the history.Validation in lieu of the PR status check: run 773 (
workflow_dispatchonfeat/effort1-auth@ff1ee47, identical content to this PR head) is fully green:checks✅gitleaks✅container✅ (the job that failed pre-fix — the smoke test now fetches/login, the anonymous entry page since auth landed)pr-reviewskipped (pull_request-only; WuMing reviewed the prior head with zero findings, and the only diff since is the workflow YAML)When PR-event dispatch recovers, a push to this branch (even empty) should populate the native status check.
Superseded — bogus merged record
This PR was created with head and base pointing at the same commit (my stacking error: the work was committed directly on
feat/multiuser, thenfeat/effort1-authwas cut at that same tip). Zero-commit, zero-file diff — unreviewable.Force-pushing the base back to its fork point (
e47d317) made Forgejo record this PR as merged, which is false: no merge occurred,feat/multiusernever contained this work. The forge refuses to reopen a merged PR, so this record stays as a tombstone.The real review happens in the successor PR. Same head (
ff1ee47), base now genuinely empty.