**COLLECTING BRANCH** Auth, sessions, and CSRF protection (Effort 1) #64
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
2 participants
Notifications
Due date
No due date set.
Reference
marvin8/yunjin!64
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. Successor to #63 (bogus auto-merged tombstone — stacking error on my part, explained there).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. Container smoke test fetches/login(the anonymous entry page since auth landed).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.checks✅gitleaks✅container✅ (dispatched viaworkflow_dispatchbecause the forge'spull_requestevent dispatch is currently wedged; see #63 comment).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)This PR is too large to review automatically (4042 lines; limit is 2000). Please consider splitting it into smaller PRs.